473,406 Members | 2,208 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,406 software developers and data experts.

stumped by tricky logic

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

Jan 29 '06 #1
3 1169
Max
Dave wrote:
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.
- Dave


Hope this helps.
--Max
Jan 29 '06 #2
"Dave" <da*********@gmail.com> wrote:
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.or...s/parsing.html, and do a bunch
of reading about parsing theory, before you sit down to start writing code.
Jan 29 '06 #3
"Dave" <da*********@gmail.com> wrote in message
news:11**********************@z14g2000cwz.googlegr oups.com...
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; }

Jan 30 '06 #4

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

10
by: Manny | last post by:
I have a web form "Page1.asp" and it reads data from a database, does some calculations, and displays the records in pages. Works fine. I have a button that displays on the page, defined as...
6
by: ccwork | last post by:
p = p->next = q; Why it is undefined? operator "=" is right-associative i.e. "p = p->next = q;" is equivalent to "p = (p->next = q); And I think another interpretation of sequence point is:...
2
by: Mark Sandfox | last post by:
I have a tricky control validation issue. I have probably designed this the wrong way but here is what I have. I have 6 TextBoxes; tbPN, tbA, tbC, tbS, tbZ, tbDOB and there are 20 of each with...
4
by: Chris Botha | last post by:
In VS2003 if I had a Web form, say Form1 and a class, say Class1, then I could pass the form as parameter to a sub/function in the class, so this would compile: Public Class Class1 Public Sub...
1
by: James R | last post by:
Ok, I'm new at this and learning as I go, but under extreme pressure from work.. So many bugs and little tricky obsticals have got me very far behind, and this is the latest.. I made a simple...
22
by: graham.parsons | last post by:
Guys, Hopefully someone can help. We have a monitoring program that has threads which start and stop monitoring at various times. There are two tables: THREADLIFECYCLE unique_id
5
by: Johnny Ljunggren | last post by:
Hello all I've got this tricky situation that I would like to solve in SQL, but don't know how to do. This is the table: Id = 3, VId = 2, Time1 = 10:00, Time2 = 14:00 Id = 4, VId = 2, Time1 =...
6
by: Nep Tune | last post by:
It goes like the following: Use AdventureWorks database and HumanResources.Department table. Create a stored procedure called spDepartmentAddUpdate. This procedure accepts two parameters: Name,...
12
by: Tom | last post by:
First, my thanks in advance to those who can help explain away this mystery line of code. Petzold is a great teacher and I appreciate his work; however, his explanation about an extra line of...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.