stumped by tricky logic

D

Dave

So I'm trying to write a CSS preprocessor.

I want to add the ability to append a selector onto other selectors.
So, given the following code:
=========================================
#selector {

{ property: value; property: value; }
.other_selector { property: value; property: value; }

#selector_2 {

.more_selector { property: value; }

}

}
=========================================

I want to return the following:
=========================================
#selector { property: value; property: value; }
#selector .other_selector { property: value; property: value; }
#selector #selector_2 .more_selector { property: value; }
=========================================

What I think I need to do is match the "{" character with its "}" pair,
then see if there's another "{" before a matching "}" - but that's
about as far as my brain will go. The actually code to make this
actually happen is not coming out when I type.

Any suggestions would be very appreciated. And handsomely rewarded (by
karma, not me).

- Dave
 
M

Max

Dave said:
So I'm trying to write a CSS preprocessor.

I want to add the ability to append a selector onto other selectors.
So, given the following code:
=========================================
#selector {

{ property: value; property: value; }
.other_selector { property: value; property: value; }

#selector_2 {

.more_selector { property: value; }

}

}
=========================================

I want to return the following:
=========================================
#selector { property: value; property: value; }
#selector .other_selector { property: value; property: value; }
#selector #selector_2 .more_selector { property: value; }
=========================================

Should the properties of #selector be "inherited" by .other_selector?
That's what I'd think the most logical approach, but by your example it
seems not.
What I think I need to do is match the "{" character with its "}" pair,
then see if there's another "{" before a matching "}" - but that's
about as far as my brain will go. The actually code to make this
actually happen is not coming out when I type.

Any suggestions would be very appreciated. And handsomely rewarded (by
karma, not me).

I'd use a class called Style or somesuch with three attributes:
-Selector (containing "#selector" for example)
-Props (containing the property: value pairs, either as-is or in a
dictionary)
-Parent (containing a reference to the style that contains this one, or
None)

Use a recursive function to read the file, and pass the containing Style
object to it. It reads the props into the class, and recurses on any
"sub-styles".

After this, you'll have constructed a tree (keep a reference to root).
Now you can use an extension of a standard pre-order traversal to output it.

Hope this helps.


--Max
 
R

Roy Smith

Dave said:
So I'm trying to write a CSS preprocessor.

What you're trying to do is write a parser. I don't know CSS, so I can't
comment on the details, but basically, you're trying to parse a non-trivial
language. It is usually hopeless to try and write a parser using just
pattern-matching. You really need to read up on grammars and parsers to do
this right. Consider, for example, something like

#selector { /* } */ foo; }

getting that right is pretty tricky, and this is far from the most complex
example possible.

I think you might want to start at
http://python.miscellaneousmirror.org/topics/parsing.html, and do a bunch
of reading about parsing theory, before you sit down to start writing code.
 
P

Paul McGuire

Dave said:
So I'm trying to write a CSS preprocessor.

I want to add the ability to append a selector onto other selectors.
So, given the following code:
=========================================
#selector {

{ property: value; property: value; }
.other_selector { property: value; property: value; }

#selector_2 {

.more_selector { property: value; }

}

}
=========================================

I want to return the following:
=========================================
#selector { property: value; property: value; }
#selector .other_selector { property: value; property: value; }
#selector #selector_2 .more_selector { property: value; }
=========================================


Dave -

Since other posters have suggested parsing, here is a pyparsing stab at your
problem. Pyparsing allows you to construct your grammar using readable
construct names, and can generate structured parse results. Pyparsing also
has built-in support for skipping over comments.

This paper describes a prior use of pyparsing to parse CSS style sheets:
http://dyomedea.com/papers/2004-extreme/paper.pdf. Google for "pyparsing
CSS" for some other possible references.

This was really more complex than I expected. The grammar was not
difficult, but the recursive routine was trickier than I thought it would
be. Hope this helps.

Download pyparsing at http://pyparsing.sourceforge.net.
-- Paul


=========================
data = """
#selector {

{ property: value; /* a nasty comment */
property: value; }
.other_selector { property: value; property: value; }

#selector_2 {
/* another nasty comment */
.more_selector { property: value; /* still another nasty
comment */ }

}

}
"""

from pyparsing import Literal,Word,Combine,Group,alphas,nums,alphanums,\
Forward,ZeroOrMore,cStyleComment,ParseResults

# define some basic symbols - suppress grouping and delimiting punctuation
# and let grouping do the rest
lbrace = Literal("{").suppress()
rbrace = Literal("}").suppress()
colon = Literal(":").suppress()
semi = Literal(";").suppress()
pound = Literal("#")
dot = Literal(".")

# define identifiers, property pattern, valid property values, and property
list
ident = Word(alphas,alphanums+"_")
pound_ident = Combine(pound + ident)
dot_ident = Combine(dot + ident)
prop_value = Word(nums) | Word(alphanums) # expand this as needed
property_def = Group( ident + colon + prop_value + semi )
prop_list = Group( lbrace + ZeroOrMore( property_def ) +
rbrace ).setResultsName("propList")

# define selector - must use Forward since selector is recursive
selector = Forward()
selector_contents = (prop_list) | Group( dot_ident.setResultsName("name") +

prop_list ) | selector
selector << Group( pound_ident.setResultsName("name") +
lbrace +
Group(ZeroOrMore(
selector_contents )).setResultsName("contents") +
rbrace )

# C-style comments should be ignored
selector.ignore(cStyleComment)

# parse the data - this only works if data *only* contains a single selector
results = selector.parseString(data)

# use pprint to display list - you can navigate the results to construct the
various selectors
import pprint
pprint.pprint( results[0].asList() )
print

# if scanning through text containing other text than just selectors,
# use scanString, which returns a generator, yielding a tuple
# for each occurrence found
#
# for results,start,end in selector.scanString(cssSourceText):
# pprint.pprint(results.asList())

# a recursive function to print out the names and property lists
def printSelector(res,namePath=[]):
if res.name != "":
subpath = namePath + [res.name]
if res.contents != "":
for c in res.contents:
printSelector(c, subpath)
elif res.propList != "":
print " ".join(subpath),"{", " ".join([ "%s : %s;" % tuple(p)
for p in res.propList ]),"}"
else:
print " ".join(subpath),"{", " ".join([ "%s : %s;" % tuple(r)
for r in res ]),"}"
else:
print " ".join(namePath),"{", " ".join([ "%s : %s;" % tuple(r) for r
in res]),"}"

printSelector( results[0] )

=========================
This prints:
['#selector',
[[['property', 'value'], ['property', 'value']],
['.other_selector', [['property', 'value'], ['property', 'value']]],
['#selector_2', [['.more_selector', [['property', 'value']]]]]]]

#selector { property : value; property : value; }
#selector .other_selector { property : value; property : value; }
#selector #selector_2 .more_selector { property : value; }
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,283
Messages
2,571,405
Members
48,098
Latest member
inno vation

Latest Threads

Top