Skip to content

Commit 8d30587

Browse files
author
Frederic Massart
committed
Primary support for comment parsing
1 parent 3877869 commit 8d30587

File tree

11 files changed

+265
-20
lines changed

11 files changed

+265
-20
lines changed

lib/Sabberworm/CSS/CSSList/CSSList.php

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,20 @@
66
use Sabberworm\CSS\RuleSet\DeclarationBlock;
77
use Sabberworm\CSS\RuleSet\RuleSet;
88
use Sabberworm\CSS\Property\Selector;
9+
use Sabberworm\CSS\Comment\Commentable;
910

1011
/**
1112
* A CSSList is the most generic container available. Its contents include RuleSet as well as other CSSList objects.
1213
* Also, it may contain Import and Charset objects stemming from @-rules.
1314
*/
14-
abstract class CSSList implements Renderable {
15+
abstract class CSSList implements Renderable, Commentable {
1516

17+
protected $aComments;
1618
protected $aContents;
1719
protected $iLineNo;
1820

1921
public function __construct($iLineNo = 0) {
22+
$this->aComments = array();
2023
$this->aContents = array();
2124
$this->iLineNo = $iLineNo;
2225
}
@@ -118,4 +121,26 @@ public abstract function isRootList();
118121
public function getContents() {
119122
return $this->aContents;
120123
}
124+
125+
/**
126+
* @param array $aComments Array of comments.
127+
*/
128+
public function addComments(array $aComments) {
129+
$this->aComments = array_merge($this->aComments, $aComments);
130+
}
131+
132+
/**
133+
* @return array
134+
*/
135+
public function getComments() {
136+
return $this->aComments;
137+
}
138+
139+
/**
140+
* @param array $aComments Array containing Comment objects.
141+
*/
142+
public function setComments(array $aComments) {
143+
$this->aComments = $aComments;
144+
}
145+
121146
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
namespace Sabberworm\CSS\Comment;
4+
5+
use Sabberworm\CSS\Renderable;
6+
7+
class Comment implements Renderable {
8+
protected $iLineNo;
9+
protected $sComment;
10+
11+
public function __construct($sComment = '', $iLineNo = 0) {
12+
$this->sComment = $sComment;
13+
$this->iLineNo = $iLineNo;
14+
}
15+
16+
/**
17+
* @return string
18+
*/
19+
public function getComment() {
20+
return $this->sComment;
21+
}
22+
23+
/**
24+
* @return int
25+
*/
26+
public function getLineNo() {
27+
return $this->iLineNo;
28+
}
29+
30+
/**
31+
* @return string
32+
*/
33+
public function setComment($sComment) {
34+
$this->sComment = $sComment;
35+
}
36+
37+
/**
38+
* @return string
39+
*/
40+
public function __toString() {
41+
return $this->render(new \Sabberworm\CSS\OutputFormat());
42+
}
43+
44+
/**
45+
* @return string
46+
*/
47+
public function render(\Sabberworm\CSS\OutputFormat $oOutputFormat) {
48+
return '/*' . $this->sComment . '*/';
49+
}
50+
51+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Sabberworm\CSS\Comment;
4+
5+
interface Commentable {
6+
7+
/**
8+
* @param array $aComments Array of comments.
9+
*/
10+
public function addComments(array $aComments);
11+
12+
/**
13+
* @return array
14+
*/
15+
public function getComments();
16+
17+
/**
18+
* @param array $aComments Array containing Comment objects.
19+
*/
20+
public function setComments(array $aComments);
21+
22+
23+
}

lib/Sabberworm/CSS/Parser.php

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Sabberworm\CSS\Value\CSSString;
2222
use Sabberworm\CSS\Rule\Rule;
2323
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
24+
use Sabberworm\CSS\Comment\Comment;
2425

2526
/**
2627
* Parser class parses CSS from text into a data structure.
@@ -83,12 +84,12 @@ public function parse() {
8384
}
8485

8586
private function parseDocument(Document $oDocument) {
86-
$this->consumeWhiteSpace();
8787
$this->parseList($oDocument, true);
8888
}
8989

9090
private function parseList(CSSList $oList, $bIsRoot = false) {
9191
while (!$this->isEnd()) {
92+
$comments = $this->consumeWhiteSpace();
9293
$oListItem = null;
9394
if($this->oParserSettings->bLenientParsing) {
9495
try {
@@ -104,9 +105,9 @@ private function parseList(CSSList $oList, $bIsRoot = false) {
104105
return;
105106
}
106107
if($oListItem) {
108+
$oListItem->setComments($comments);
107109
$oList->append($oListItem);
108110
}
109-
$this->consumeWhiteSpace();
110111
}
111112
if (!$bIsRoot) {
112113
throw new SourceException("Unexpected end of document", $this->iLineNo);
@@ -161,7 +162,6 @@ private function parseAtRule() {
161162
$oResult = new KeyFrame($iIdentifierLineNum);
162163
$oResult->setVendorKeyFrame($sIdentifier);
163164
$oResult->setAnimationName(trim($this->consumeUntil('{', false, true)));
164-
$this->consumeWhiteSpace();
165165
$this->parseList($oResult);
166166
return $oResult;
167167
} else if ($sIdentifier === 'namespace') {
@@ -182,7 +182,6 @@ private function parseAtRule() {
182182
} else {
183183
//Unknown other at rule (font-face or such)
184184
$sArgs = trim($this->consumeUntil('{', false, true));
185-
$this->consumeWhiteSpace();
186185
$bUseRuleSet = true;
187186
foreach($this->blockRules as $sBlockRuleName) {
188187
if($this->identifierIs($sIdentifier, $sBlockRuleName)) {
@@ -299,17 +298,17 @@ private function parseCharacter($bIsForIdentifier) {
299298
}
300299

301300
private function parseSelector() {
301+
$aComments = $this->consumeWhiteSpace();
302302
$oResult = new DeclarationBlock($this->iLineNo);
303303
$oResult->setSelector($this->consumeUntil('{', false, true));
304-
$this->consumeWhiteSpace();
304+
$oResult->setComments($aComments);
305305
$this->parseRuleSet($oResult);
306306
return $oResult;
307307
}
308308

309309
private function parseRuleSet($oRuleSet) {
310310
while ($this->comes(';')) {
311311
$this->consume(';');
312-
$this->consumeWhiteSpace();
313312
}
314313
while (!$this->comes('}')) {
315314
$oRule = null;
@@ -323,7 +322,6 @@ private function parseRuleSet($oRuleSet) {
323322
if($this->streql(substr($sConsume, -1), '}')) {
324323
--$this->iCurrentPosition;
325324
} else {
326-
$this->consumeWhiteSpace();
327325
while ($this->comes(';')) {
328326
$this->consume(';');
329327
}
@@ -339,14 +337,15 @@ private function parseRuleSet($oRuleSet) {
339337
if($oRule) {
340338
$oRuleSet->addRule($oRule);
341339
}
342-
$this->consumeWhiteSpace();
343340
}
344341
$this->consume('}');
345342
}
346343

347344
private function parseRule() {
345+
$aComments = $this->consumeWhiteSpace();
348346
$oRule = new Rule($this->parseIdentifier(), $this->iLineNo);
349-
$this->consumeWhiteSpace();
347+
$oRule->setComments($aComments);
348+
$oRule->addComments($this->consumeWhiteSpace());
350349
$this->consume(':');
351350
$oValue = $this->parseValue(self::listDelimiterForRule($oRule->getRule()));
352351
$oRule->setValue($oValue);
@@ -358,7 +357,6 @@ private function parseRule() {
358357
}
359358
while ($this->comes(';')) {
360359
$this->consume(';');
361-
$this->consumeWhiteSpace();
362360
}
363361
return $oRule;
364362
}
@@ -557,35 +555,53 @@ private function consumeExpression($mExpression) {
557555
}
558556

559557
private function consumeWhiteSpace() {
558+
$comments = array();
560559
do {
561560
while (preg_match('/\\s/isSu', $this->peek()) === 1) {
562561
$this->consume(1);
563562
}
564563
if($this->oParserSettings->bLenientParsing) {
565564
try {
566-
$bHasComment = $this->consumeComment();
565+
$oComment = $this->consumeComment();
567566
} catch(UnexpectedTokenException $e) {
568567
// When we can’t find the end of a comment, we assume the document is finished.
569568
$this->iCurrentPosition = $this->iLength;
570569
return;
571570
}
572571
} else {
573-
$bHasComment = $this->consumeComment();
572+
$oComment = $this->consumeComment();
573+
}
574+
if ($oComment !== false) {
575+
$comments[] = $oComment;
574576
}
575-
} while($bHasComment);
577+
} while($oComment !== false);
578+
return $comments;
576579
}
577580

581+
/**
582+
* @return false|Comment
583+
*/
578584
private function consumeComment() {
585+
$mComment = false;
579586
if ($this->comes('/*')) {
587+
$iLineNo = $this->iLineNo;
580588
$this->consume(1);
581-
while ($this->consume(1) !== '') {
589+
$mComment = '';
590+
while (($char = $this->consume(1)) !== '') {
591+
$mComment .= $char;
582592
if ($this->comes('*/')) {
583593
$this->consume(2);
584-
return true;
594+
break;
585595
}
586596
}
587597
}
588-
return false;
598+
599+
if ($mComment !== false) {
600+
// We skip the * which was included in the comment.
601+
return new Comment(substr($mComment, 1), $iLineNo);
602+
}
603+
604+
return $mComment;
589605
}
590606

591607
private function isEnd() {

lib/Sabberworm/CSS/Property/AtRule.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
namespace Sabberworm\CSS\Property;
44

55
use Sabberworm\CSS\Renderable;
6+
use Sabberworm\CSS\Comment\Commentable;
67

7-
interface AtRule extends Renderable {
8+
interface AtRule extends Renderable, Commentable {
89
const BLOCK_RULES = 'media/document/supports/region-style/font-feature-values';
910
// Since there are more set rules than block rules, we’re whitelisting the block rules and have anything else be treated as a set rule.
1011
const SET_RULES = 'font-face/counter-style/page/swash/styleset/annotation'; //…and more font-specific ones (to be used inside font-feature-values)

lib/Sabberworm/CSS/Property/CSSNamespace.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ class CSSNamespace implements AtRule {
99
private $mUrl;
1010
private $sPrefix;
1111
private $iLineNo;
12+
protected $aComments;
1213

1314
public function __construct($mUrl, $sPrefix = null, $iLineNo = 0) {
1415
$this->mUrl = $mUrl;
1516
$this->sPrefix = $sPrefix;
1617
$this->iLineNo = $iLineNo;
18+
$this->aComments = array();
1719
}
1820

1921
/**
@@ -58,4 +60,16 @@ public function atRuleArgs() {
5860
}
5961
return $aResult;
6062
}
63+
64+
public function addComments(array $aComments) {
65+
$this->aComments = array_merge($this->aComments, $aComments);
66+
}
67+
68+
public function getComments() {
69+
return $this->aComments;
70+
}
71+
72+
public function setComments(array $aComments) {
73+
$this->aComments = $aComments;
74+
}
6175
}

lib/Sabberworm/CSS/Property/Charset.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ class Charset implements AtRule {
1313

1414
private $sCharset;
1515
protected $iLineNo;
16+
protected $aComment;
1617

1718
public function __construct($sCharset, $iLineNo = 0) {
1819
$this->sCharset = $sCharset;
1920
$this->iLineNo = $iLineNo;
21+
$this->aComments = array();
2022
}
2123

2224
/**
@@ -49,4 +51,16 @@ public function atRuleName() {
4951
public function atRuleArgs() {
5052
return $this->sCharset;
5153
}
54+
55+
public function addComments(array $aComments) {
56+
$this->aComments = array_merge($this->aComments, $aComments);
57+
}
58+
59+
public function getComments() {
60+
return $this->aComments;
61+
}
62+
63+
public function setComments(array $aComments) {
64+
$this->aComments = $aComments;
65+
}
5266
}

lib/Sabberworm/CSS/Property/Import.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ class Import implements AtRule {
1111
private $oLocation;
1212
private $sMediaQuery;
1313
protected $iLineNo;
14+
protected $aComments;
1415

1516
public function __construct(URL $oLocation, $sMediaQuery, $iLineNo = 0) {
1617
$this->oLocation = $oLocation;
1718
$this->sMediaQuery = $sMediaQuery;
1819
$this->iLineNo = $iLineNo;
20+
$this->aComments = array();
1921
}
2022

2123
/**
@@ -52,4 +54,16 @@ public function atRuleArgs() {
5254
}
5355
return $aResult;
5456
}
57+
58+
public function addComments(array $aComments) {
59+
$this->aComments = array_merge($this->aComments, $aComments);
60+
}
61+
62+
public function getComments() {
63+
return $this->aComments;
64+
}
65+
66+
public function setComments(array $aComments) {
67+
$this->aComments = $aComments;
68+
}
5569
}

0 commit comments

Comments
 (0)