Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
csscom
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Taddeüs Kroes
csscom
Commits
cc85a34e
Commit
cc85a34e
authored
Dec 31, 2012
by
Taddeüs Kroes
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added unit tests for parser and fixed a lot of bugs on the way
parent
596e89fd
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
268 additions
and
26 deletions
+268
-26
parse.py
parse.py
+54
-25
tests/test_parse.py
tests/test_parse.py
+214
-1
No files found.
parse.py
View file @
cc85a34e
...
...
@@ -3,7 +3,7 @@ def split_selectors(raw_selector):
Split a selector with commas and arbitrary whitespaces into a list of
selector swith single-space whitespaces.
"""
return
[
' '
.
join
(
s
.
split
())
for
s
in
raw_selector
.
split
(
','
)]
return
filter
(
None
,
(
' '
.
join
(
s
.
split
())
for
s
in
raw_selector
.
split
(
','
)))
def
parse_groups
(
css
):
...
...
@@ -16,20 +16,12 @@ def parse_groups(css):
prev_char
=
None
lineno
=
1
properties
=
[]
current_group
=
root_group
=
[]
groups
=
[
(
None
,
root_group
)
]
current_group
=
(
None
,
[])
groups
=
[]
selectors
=
None
comment
=
False
def
parse_property
():
assert
selectors
is
not
None
if
stack
.
strip
():
parts
=
stack
.
split
(
':'
,
1
)
assert
len
(
parts
)
==
2
name
,
value
=
map
(
str
.
strip
,
parts
)
assert
'
\
n
'
not
in
name
properties
.
append
((
name
,
value
))
nesting_level
=
0
property_name
=
None
try
:
for
c
in
css
:
...
...
@@ -43,32 +35,59 @@ def parse_groups(css):
if
char
==
'/'
and
prev_char
==
'*'
:
comment
=
False
elif
char
==
'{'
:
nesting_level
+=
1
# Block start
if
selectors
is
not
None
:
# Block is nested, save group selector
current_group
=
[]
groups
.
append
((
selectors
,
current_group
))
# Block is nested, push current root group and continue
# with this group
if
len
(
current_group
[
1
]):
groups
.
append
(
current_group
)
current_group
=
(
selectors
,
[])
selectors
=
split_selectors
(
stack
)
#print stack.strip(), '->', selectors
stack
=
''
assert
len
(
selectors
)
elif
char
==
'}'
:
# Last property may not have been closed with a semicolon
parse_property
()
assert
nesting_level
>
0
nesting_level
-=
1
if
selectors
is
None
:
# Closing group
current_group
=
root_group
groups
.
append
(
current_group
)
current_group
=
(
None
,
[])
else
:
# Closing block
current_group
.
append
((
selectors
,
properties
))
# Last property may not have been closed with a semicolon
property_value
=
stack
.
strip
()
if
len
(
property_value
):
assert
property_name
is
not
None
properties
.
append
((
property_name
,
property_value
))
property_name
=
None
stack
=
''
current_group
[
1
].
append
((
selectors
,
properties
))
selectors
=
None
properties
=
[]
elif
char
==
';'
:
# Property definition
parse_property
()
elif
char
==
':'
and
nesting_level
>
0
:
assert
selectors
is
not
None
# Property name
property_name
=
stack
.
strip
()
assert
'
\
n
'
not
in
property_name
stack
=
''
elif
char
==
';'
:
# Property value
property_value
=
stack
.
strip
()
if
len
(
property_value
):
assert
property_name
is
not
None
properties
.
append
((
property_name
,
property_value
))
property_name
=
None
stack
=
''
else
:
assert
property_name
is
None
elif
char
==
'*'
and
prev_char
==
'/'
:
# Comment start
comment
=
True
...
...
@@ -77,7 +96,17 @@ def parse_groups(css):
stack
+=
char
prev_char
=
char
if
len
(
current_group
[
1
]):
groups
.
append
(
current_group
)
if
stack
.
split
()
or
nesting_level
>
0
:
char
=
'<EOF>'
raise
AssertionError
()
except
AssertionError
:
raise
Exception
(
'unexpected
\
'
%c
\
'
on line %d'
%
(
char
,
lineno
))
if
len
(
char
)
<
2
:
char
=
"'"
+
char
+
"'"
raise
Exception
(
'unexpected %s on line %d'
%
(
char
,
lineno
))
return
groups
tests/test_parse.py
View file @
cc85a34e
...
...
@@ -3,10 +3,223 @@ from unittest import TestCase
from
parse
import
split_selectors
,
parse_groups
class
Test
Parse
(
TestCase
):
class
Test
Helpers
(
TestCase
):
def
test_split_selectors
(
self
):
self
.
assertEqual
(
split_selectors
(
''
),
[])
self
.
assertEqual
(
split_selectors
(
'a, b'
),
[
'a'
,
'b'
])
self
.
assertEqual
(
split_selectors
(
'a ,b'
),
[
'a'
,
'b'
])
self
.
assertEqual
(
split_selectors
(
'
\
n
a ,b '
),
[
'a'
,
'b'
])
self
.
assertEqual
(
split_selectors
(
'a, b
\
n
c'
),
[
'a'
,
'b c'
])
self
.
assertEqual
(
split_selectors
(
'a,
\
n
b c ,d
\
n
'
),
[
'a'
,
'b c'
,
'd'
])
class
TestParseGroups
(
TestCase
):
def
test_empty_stylesheet
(
self
):
self
.
assertParse
(
''
,
[])
self
.
assertParse
(
'
\
n
'
,
[])
def
test_empty_block
(
self
):
self
.
assertParse
(
'div {}'
,
[(
None
,
[([
'div'
],
[])])])
def
test_single_property
(
self
):
self
.
assertParse
(
'div {color:black;}'
,
[(
None
,
[([
'div'
],
[(
'color'
,
'black'
)])])])
self
.
assertParse
(
'div {color:black}'
,
[(
None
,
[([
'div'
],
[(
'color'
,
'black'
)])])])
self
.
assertParse
(
'''
div {
color: black;
}
'''
,
[(
None
,
[([
'div'
],
[(
'color'
,
'black'
)])])])
self
.
assertParse
(
'''
div {
color: black
}
'''
,
[(
None
,
[([
'div'
],
[(
'color'
,
'black'
)])])])
def
test_multiple_properties
(
self
):
self
.
assertParse
(
'''
div {
color: black;
border: none;
}
'''
,
[(
None
,
[([
'div'
],
[(
'color'
,
'black'
),
(
'border'
,
'none'
)])])])
self
.
assertParse
(
'''
div {
color: black;
border: none
}
'''
,
[(
None
,
[([
'div'
],
[(
'color'
,
'black'
),
(
'border'
,
'none'
)])])])
def
test_multiple_selectors
(
self
):
self
.
assertParse
(
'div,p {color: black}'
,
[(
None
,
[([
'div'
,
'p'
],
[(
'color'
,
'black'
)])])])
self
.
assertParse
(
'div, p {color: black}'
,
[(
None
,
[([
'div'
,
'p'
],
[(
'color'
,
'black'
)])])])
self
.
assertParse
(
'p,
\
n
\
t
div {color: black}'
,
[(
None
,
[([
'p'
,
'div'
],
[(
'color'
,
'black'
)])])])
def
test_single_group
(
self
):
self
.
assertParse
(
'''
@media (min-width: 970px) {
div {color: black}
}
'''
,
[([
'@media (min-width: 970px)'
],
[([
'div'
],
[(
'color'
,
'black'
)])])])
def
test_single_group_multiple_blocks
(
self
):
self
.
assertParse
(
'''
@media (min-width: 970px) {
div {color: black}
p {margin: 0}
}
'''
,
[([
'@media (min-width: 970px)'
],
[([
'div'
],
[(
'color'
,
'black'
)]),
([
'p'
],
[(
'margin'
,
'0'
)])])])
def
test_multiple_groups
(
self
):
self
.
assertParse
(
'''
@media (min-width: 970px) {
div {color: black}
}
@media (max-width: 969px) {
div {color: red}
}
'''
,
[
([
'@media (min-width: 970px)'
],
[([
'div'
],
[(
'color'
,
'black'
)])]),
([
'@media (max-width: 969px)'
],
[([
'div'
],
[(
'color'
,
'red'
)])])
])
def
test_group_with_root
(
self
):
self
.
assertParse
(
'''
div {
color: black
}
@media (max-width: 969px) {
div {color: red}
}
'''
,
[
(
None
,
[([
'div'
],
[(
'color'
,
'black'
)])]),
([
'@media (max-width: 969px)'
],
[([
'div'
],
[(
'color'
,
'red'
)])])
])
self
.
assertParse
(
'''
@media (max-width: 969px) {
div {color: red}
}
div {
color: black
}
'''
,
[
([
'@media (max-width: 969px)'
],
[([
'div'
],
[(
'color'
,
'red'
)])]),
(
None
,
[([
'div'
],
[(
'color'
,
'black'
)])])
])
def
test_group_with_multiple_roots
(
self
):
self
.
assertParse
(
'''
div {
color: black
}
@media (max-width: 969px) {
div {color: red}
}
p {
margin: 0
}
'''
,
[
(
None
,
[([
'div'
],
[(
'color'
,
'black'
)])]),
([
'@media (max-width: 969px)'
],
[([
'div'
],
[(
'color'
,
'red'
)])]),
(
None
,
[([
'p'
],
[(
'margin'
,
'0'
)])])
])
def
test_multiple_groups_multiple_roots
(
self
):
self
.
assertParse
(
'''
@media (min-width: 970px) {
div {color: black}
}
div {
color: black
}
@media (max-width: 969px) {
div {color: red}
}
p {
margin: 0
}
'''
,
[
([
'@media (min-width: 970px)'
],
[([
'div'
],
[(
'color'
,
'black'
)])]),
(
None
,
[([
'div'
],
[(
'color'
,
'black'
)])]),
([
'@media (max-width: 969px)'
],
[([
'div'
],
[(
'color'
,
'red'
)])]),
(
None
,
[([
'p'
],
[(
'margin'
,
'0'
)])])
])
self
.
assertParse
(
'''
p {
margin: 0
}
@media (min-width: 970px) {
div {color: black}
}
div {
color: black
}
@media (max-width: 969px) {
div {color: red}
}
'''
,
[
(
None
,
[([
'p'
],
[(
'margin'
,
'0'
)])]),
([
'@media (min-width: 970px)'
],
[([
'div'
],
[(
'color'
,
'black'
)])]),
(
None
,
[([
'div'
],
[(
'color'
,
'black'
)])]),
([
'@media (max-width: 969px)'
],
[([
'div'
],
[(
'color'
,
'red'
)])]),
])
def
test_comment
(
self
):
self
.
assertParse
(
'''
/* this is a comment */
div {color: black}
'''
,
[(
None
,
[([
'div'
],
[(
'color'
,
'black'
)])])])
self
.
assertParse
(
'''
/*
* this is a multiline comment
*/
div {color: black}
'''
,
[(
None
,
[([
'div'
],
[(
'color'
,
'black'
)])])])
self
.
assertParse
(
'''
div {color: /*inline comment*/ black}
'''
,
[(
None
,
[([
'div'
],
[(
'color'
,
'black'
)])])])
def
test_error_brackets
(
self
):
self
.
assertRaisesRegexp
(
Exception
,
'unexpected
\
'
{
\
'
on line 1'
,
parse_groups
,
'{'
)
self
.
assertRaisesRegexp
(
Exception
,
'unexpected
\
'
{
\
'
on line 2'
,
parse_groups
,
'div {
\
n
{}'
)
self
.
assertRaisesRegexp
(
Exception
,
'unexpected
\
'
}
\
'
on line 1'
,
parse_groups
,
'}'
)
self
.
assertRaisesRegexp
(
Exception
,
'unexpected
\
'
}
\
'
on line 2'
,
parse_groups
,
'div
\
n
}'
)
def
test_error_EOF
(
self
):
self
.
assertRaisesRegexp
(
Exception
,
'unexpected <EOF> on line 3'
,
parse_groups
,
'
\
n
div
\
n
'
)
self
.
assertRaisesRegexp
(
Exception
,
'unexpected <EOF> on line 2'
,
parse_groups
,
'
\
n
div {'
)
def
test_error_property_name
(
self
):
self
.
assertRaisesRegexp
(
Exception
,
'unexpected
\
'
:
\
'
on line 2'
,
parse_groups
,
'div{margin
\
n
left: 0}'
)
def
test_error_property_value
(
self
):
self
.
assertRaisesRegexp
(
Exception
,
'unexpected
\
'
;
\
'
on line 1'
,
parse_groups
,
'div{margin:;}'
)
self
.
assertRaisesRegexp
(
Exception
,
'unexpected
\
'
;
\
'
on line 1'
,
parse_groups
,
'div{foo;}'
)
self
.
assertParse
(
'div {;}'
,
[(
None
,
[([
'div'
],
[])])])
def
assertParse
(
self
,
css
,
result
):
self
.
assertEqual
(
parse_groups
(
css
),
result
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment