5
5
use Sabberworm \CSS \CSSList \CSSList ;
6
6
use Sabberworm \CSS \CSSList \Document ;
7
7
use Sabberworm \CSS \CSSList \KeyFrame ;
8
+ use Sabberworm \CSS \Parsing \SourceException ;
8
9
use Sabberworm \CSS \Property \AtRule ;
9
10
use Sabberworm \CSS \Property \Import ;
10
11
use Sabberworm \CSS \Property \Charset ;
@@ -34,10 +35,20 @@ class Parser {
34
35
private $ iLength ;
35
36
private $ blockRules ;
36
37
private $ aSizeUnits ;
38
+ private $ iLineNo ;
37
39
38
- public function __construct ($ sText , Settings $ oParserSettings = null ) {
40
+ /**
41
+ * Parser constructor.
42
+ * Note that that iLineNo starts from 1 and not 0
43
+ *
44
+ * @param $sText
45
+ * @param Settings|null $oParserSettings
46
+ * @param int $iLineNo
47
+ */
48
+ public function __construct ($ sText , Settings $ oParserSettings = null , $ iLineNo = 1 ) {
39
49
$ this ->sText = $ sText ;
40
50
$ this ->iCurrentPosition = 0 ;
51
+ $ this ->iLineNo = $ iLineNo ;
41
52
if ($ oParserSettings === null ) {
42
53
$ oParserSettings = Settings::create ();
43
54
}
@@ -66,7 +77,7 @@ public function getCharset() {
66
77
67
78
public function parse () {
68
79
$ this ->setCharset ($ this ->oParserSettings ->sDefaultCharset );
69
- $ oResult = new Document ();
80
+ $ oResult = new Document ($ this -> iLineNo );
70
81
$ this ->parseDocument ($ oResult );
71
82
return $ oResult ;
72
83
}
@@ -98,7 +109,7 @@ private function parseList(CSSList $oList, $bIsRoot = false) {
98
109
$ this ->consumeWhiteSpace ();
99
110
}
100
111
if (!$ bIsRoot ) {
101
- throw new \ Exception ("Unexpected end of document " );
112
+ throw new SourceException ("Unexpected end of document " , $ this -> iLineNo );
102
113
}
103
114
}
104
115
@@ -107,18 +118,18 @@ private function parseListItem(CSSList $oList, $bIsRoot = false) {
107
118
$ oAtRule = $ this ->parseAtRule ();
108
119
if ($ oAtRule instanceof Charset) {
109
120
if (!$ bIsRoot ) {
110
- throw new UnexpectedTokenException ('@charset may only occur in root document ' , '' , 'custom ' );
121
+ throw new UnexpectedTokenException ('@charset may only occur in root document ' , '' , 'custom ' , $ this -> iLineNo );
111
122
}
112
123
if (count ($ oList ->getContents ()) > 0 ) {
113
- throw new UnexpectedTokenException ('@charset must be the first parseable token in a document ' , '' , 'custom ' );
124
+ throw new UnexpectedTokenException ('@charset must be the first parseable token in a document ' , '' , 'custom ' , $ this -> iLineNo );
114
125
}
115
126
$ this ->setCharset ($ oAtRule ->getCharset ()->getString ());
116
127
}
117
128
return $ oAtRule ;
118
129
} else if ($ this ->comes ('} ' )) {
119
130
$ this ->consume ('} ' );
120
131
if ($ bIsRoot ) {
121
- throw new \ Exception ("Unopened { " );
132
+ throw new SourceException ("Unopened { " , $ this -> iLineNo );
122
133
} else {
123
134
return null ;
124
135
}
@@ -130,6 +141,7 @@ private function parseListItem(CSSList $oList, $bIsRoot = false) {
130
141
private function parseAtRule () {
131
142
$ this ->consume ('@ ' );
132
143
$ sIdentifier = $ this ->parseIdentifier ();
144
+ $ iIdentifierLineNum = $ this ->iLineNo ;
133
145
$ this ->consumeWhiteSpace ();
134
146
if ($ sIdentifier === 'import ' ) {
135
147
$ oLocation = $ this ->parseURLValue ();
@@ -139,14 +151,14 @@ private function parseAtRule() {
139
151
$ sMediaQuery = $ this ->consumeUntil ('; ' );
140
152
}
141
153
$ this ->consume ('; ' );
142
- return new Import ($ oLocation , $ sMediaQuery );
154
+ return new Import ($ oLocation , $ sMediaQuery, $ iIdentifierLineNum );
143
155
} else if ($ sIdentifier === 'charset ' ) {
144
156
$ sCharset = $ this ->parseStringValue ();
145
157
$ this ->consumeWhiteSpace ();
146
158
$ this ->consume ('; ' );
147
- return new Charset ($ sCharset );
159
+ return new Charset ($ sCharset, $ iIdentifierLineNum );
148
160
} else if ($ this ->identifierIs ($ sIdentifier , 'keyframes ' )) {
149
- $ oResult = new KeyFrame ();
161
+ $ oResult = new KeyFrame ($ iIdentifierLineNum );
150
162
$ oResult ->setVendorKeyFrame ($ sIdentifier );
151
163
$ oResult ->setAnimationName (trim ($ this ->consumeUntil ('{ ' , false , true )));
152
164
$ this ->consumeWhiteSpace ();
@@ -161,12 +173,12 @@ private function parseAtRule() {
161
173
}
162
174
$ this ->consume ('; ' );
163
175
if ($ sPrefix !== null && !is_string ($ sPrefix )) {
164
- throw new UnexpectedTokenException ('Wrong namespace prefix ' , $ sPrefix , 'custom ' );
176
+ throw new UnexpectedTokenException ('Wrong namespace prefix ' , $ sPrefix , 'custom ' , $ iIdentifierLineNum );
165
177
}
166
178
if (!($ mUrl instanceof CSSString || $ mUrl instanceof URL )) {
167
- throw new UnexpectedTokenException ('Wrong namespace url of invalid type ' , $ mUrl , 'custom ' );
179
+ throw new UnexpectedTokenException ('Wrong namespace url of invalid type ' , $ mUrl , 'custom ' , $ iIdentifierLineNum );
168
180
}
169
- return new CSSNamespace ($ mUrl , $ sPrefix );
181
+ return new CSSNamespace ($ mUrl , $ sPrefix, $ iIdentifierLineNum );
170
182
} else {
171
183
//Unknown other at rule (font-face or such)
172
184
$ sArgs = trim ($ this ->consumeUntil ('{ ' , false , true ));
@@ -179,10 +191,10 @@ private function parseAtRule() {
179
191
}
180
192
}
181
193
if ($ bUseRuleSet ) {
182
- $ oAtRule = new AtRuleSet ($ sIdentifier , $ sArgs );
194
+ $ oAtRule = new AtRuleSet ($ sIdentifier , $ sArgs, $ iIdentifierLineNum );
183
195
$ this ->parseRuleSet ($ oAtRule );
184
196
} else {
185
- $ oAtRule = new AtRuleBlockList ($ sIdentifier , $ sArgs );
197
+ $ oAtRule = new AtRuleBlockList ($ sIdentifier , $ sArgs, $ iIdentifierLineNum );
186
198
$ this ->parseList ($ oAtRule );
187
199
}
188
200
return $ oAtRule ;
@@ -192,7 +204,7 @@ private function parseAtRule() {
192
204
private function parseIdentifier ($ bAllowFunctions = true , $ bIgnoreCase = true ) {
193
205
$ sResult = $ this ->parseCharacter (true );
194
206
if ($ sResult === null ) {
195
- throw new UnexpectedTokenException ($ sResult , $ this ->peek (5 ), 'identifier ' );
207
+ throw new UnexpectedTokenException ($ sResult , $ this ->peek (5 ), 'identifier ' , $ this -> iLineNo );
196
208
}
197
209
$ sCharacter = null ;
198
210
while (($ sCharacter = $ this ->parseCharacter (true )) !== null ) {
@@ -204,7 +216,7 @@ private function parseIdentifier($bAllowFunctions = true, $bIgnoreCase = true) {
204
216
if ($ bAllowFunctions && $ this ->comes ('( ' )) {
205
217
$ this ->consume ('( ' );
206
218
$ aArguments = $ this ->parseValue (array ('= ' , ' ' , ', ' ));
207
- $ sResult = new CSSFunction ($ sResult , $ aArguments );
219
+ $ sResult = new CSSFunction ($ sResult , $ aArguments, ' , ' , $ this -> iLineNo );
208
220
$ this ->consume (') ' );
209
221
}
210
222
return $ sResult ;
@@ -232,13 +244,13 @@ private function parseStringValue() {
232
244
while (!$ this ->comes ($ sQuote )) {
233
245
$ sContent = $ this ->parseCharacter (false );
234
246
if ($ sContent === null ) {
235
- throw new \ Exception ("Non-well-formed quoted string {$ this ->peek (3 )}" );
247
+ throw new SourceException ("Non-well-formed quoted string {$ this ->peek (3 )}" , $ this -> iLineNo );
236
248
}
237
249
$ sResult .= $ sContent ;
238
250
}
239
251
$ this ->consume ($ sQuote );
240
252
}
241
- return new CSSString ($ sResult );
253
+ return new CSSString ($ sResult, $ this -> iLineNo );
242
254
}
243
255
244
256
private function parseCharacter ($ bIsForIdentifier ) {
@@ -287,7 +299,7 @@ private function parseCharacter($bIsForIdentifier) {
287
299
}
288
300
289
301
private function parseSelector () {
290
- $ oResult = new DeclarationBlock ();
302
+ $ oResult = new DeclarationBlock ($ this -> iLineNo );
291
303
$ oResult ->setSelector ($ this ->consumeUntil ('{ ' , false , true ));
292
304
$ this ->consumeWhiteSpace ();
293
305
$ this ->parseRuleSet ($ oResult );
@@ -333,7 +345,7 @@ private function parseRuleSet($oRuleSet) {
333
345
}
334
346
335
347
private function parseRule () {
336
- $ oRule = new Rule ($ this ->parseIdentifier ());
348
+ $ oRule = new Rule ($ this ->parseIdentifier (), $ this -> iLineNo );
337
349
$ this ->consumeWhiteSpace ();
338
350
$ this ->consume (': ' );
339
351
$ oValue = $ this ->parseValue (self ::listDelimiterForRule ($ oRule ->getRule ()));
@@ -387,7 +399,7 @@ private function parseValue($aListDelimiters) {
387
399
break ;
388
400
}
389
401
}
390
- $ oList = new RuleValueList ($ sDelimiter );
402
+ $ oList = new RuleValueList ($ sDelimiter, $ this -> iLineNo );
391
403
for ($ i = $ iStartPosition - 1 ; $ i - $ iStartPosition + 1 < $ iLength * 2 ; $ i +=2 ) {
392
404
$ oList ->addListComponent ($ aStack [$ i ]);
393
405
}
@@ -445,7 +457,7 @@ private function parseNumericValue($bForColor = false) {
445
457
}
446
458
}
447
459
}
448
- return new Size (floatval ($ sSize ), $ sUnit , $ bForColor );
460
+ return new Size (floatval ($ sSize ), $ sUnit , $ bForColor, $ this -> iLineNo );
449
461
}
450
462
451
463
private function parseColorValue () {
@@ -456,7 +468,7 @@ private function parseColorValue() {
456
468
if ($ this ->strlen ($ sValue ) === 3 ) {
457
469
$ sValue = $ sValue [0 ] . $ sValue [0 ] . $ sValue [1 ] . $ sValue [1 ] . $ sValue [2 ] . $ sValue [2 ];
458
470
}
459
- $ aColor = array ('r ' => new Size (intval ($ sValue [0 ] . $ sValue [1 ], 16 ), null , true ), 'g ' => new Size (intval ($ sValue [2 ] . $ sValue [3 ], 16 ), null , true ), 'b ' => new Size (intval ($ sValue [4 ] . $ sValue [5 ], 16 ), null , true ));
471
+ $ aColor = array ('r ' => new Size (intval ($ sValue [0 ] . $ sValue [1 ], 16 ), null , true , $ this -> iLineNo ), 'g ' => new Size (intval ($ sValue [2 ] . $ sValue [3 ], 16 ), null , true , $ this -> iLineNo ), 'b ' => new Size (intval ($ sValue [4 ] . $ sValue [5 ], 16 ), null , true , $ this -> iLineNo ));
460
472
} else {
461
473
$ sColorMode = $ this ->parseIdentifier (false );
462
474
$ this ->consumeWhiteSpace ();
@@ -472,7 +484,7 @@ private function parseColorValue() {
472
484
}
473
485
$ this ->consume (') ' );
474
486
}
475
- return new Color ($ aColor );
487
+ return new Color ($ aColor, $ this -> iLineNo );
476
488
}
477
489
478
490
private function parseURLValue () {
@@ -483,7 +495,7 @@ private function parseURLValue() {
483
495
$ this ->consume ('( ' );
484
496
}
485
497
$ this ->consumeWhiteSpace ();
486
- $ oResult = new URL ($ this ->parseStringValue ());
498
+ $ oResult = new URL ($ this ->parseStringValue (), $ this -> iLineNo );
487
499
if ($ bUseUrl ) {
488
500
$ this ->consumeWhiteSpace ();
489
501
$ this ->consume (') ' );
@@ -516,17 +528,21 @@ private function peek($iLength = 1, $iOffset = 0) {
516
528
517
529
private function consume ($ mValue = 1 ) {
518
530
if (is_string ($ mValue )) {
531
+ $ iLineCount = substr_count ($ mValue , "\n" );
519
532
$ iLength = $ this ->strlen ($ mValue );
520
533
if (!$ this ->streql ($ this ->substr ($ this ->iCurrentPosition , $ iLength ), $ mValue )) {
521
- throw new UnexpectedTokenException ($ mValue , $ this ->peek (max ($ iLength , 5 )));
534
+ throw new UnexpectedTokenException ($ mValue , $ this ->peek (max ($ iLength , 5 )), $ this -> iLineNo );
522
535
}
536
+ $ this ->iLineNo += $ iLineCount ;
523
537
$ this ->iCurrentPosition += $ this ->strlen ($ mValue );
524
538
return $ mValue ;
525
539
} else {
526
540
if ($ this ->iCurrentPosition + $ mValue > $ this ->iLength ) {
527
- throw new UnexpectedTokenException ($ mValue , $ this ->peek (5 ), 'count ' );
541
+ throw new UnexpectedTokenException ($ mValue , $ this ->peek (5 ), 'count ' , $ this -> iLineNo );
528
542
}
529
543
$ sResult = $ this ->substr ($ this ->iCurrentPosition , $ mValue );
544
+ $ iLineCount = substr_count ($ sResult , "\n" );
545
+ $ this ->iLineNo += $ iLineCount ;
530
546
$ this ->iCurrentPosition += $ mValue ;
531
547
return $ sResult ;
532
548
}
@@ -537,7 +553,7 @@ private function consumeExpression($mExpression) {
537
553
if (preg_match ($ mExpression , $ this ->inputLeft (), $ aMatches , PREG_OFFSET_CAPTURE ) === 1 ) {
538
554
return $ this ->consume ($ aMatches [0 ][0 ]);
539
555
}
540
- throw new UnexpectedTokenException ($ mExpression , $ this ->peek (5 ), 'expression ' );
556
+ throw new UnexpectedTokenException ($ mExpression , $ this ->peek (5 ), 'expression ' , $ this -> iLineNo );
541
557
}
542
558
543
559
private function consumeWhiteSpace () {
@@ -595,7 +611,7 @@ private function consumeUntil($aEnd, $bIncludeEnd = false, $consumeEnd = false)
595
611
}
596
612
597
613
$ this ->iCurrentPosition = $ start ;
598
- throw new UnexpectedTokenException ('One of (" ' .implode ('"," ' , $ aEnd ).'") ' , $ this ->peek (5 ), 'search ' );
614
+ throw new UnexpectedTokenException ('One of (" ' .implode ('"," ' , $ aEnd ).'") ' , $ this ->peek (5 ), 'search ' , $ this -> iLineNo );
599
615
}
600
616
601
617
private function inputLeft () {
0 commit comments