stumped by tricky logic

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Dave

    #1

    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

  • Max

    #2
    Re: stumped by tricky logic

    Dave wrote:[color=blue]
    > 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; }
    >
    > }
    >
    > }[/color]
    [color=blue]
    > =============== =============== ===========
    >
    > 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; }
    > =============== =============== ===========
    >[/color]

    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.
    [color=blue]
    > 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).
    >[/color]

    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.
    [color=blue]
    > - Dave
    >[/color]

    Hope this helps.


    --Max

    Comment

    • Roy Smith

      #3
      Re: stumped by tricky logic

      "Dave" <davidworley@gm ail.com> wrote:[color=blue]
      > So I'm trying to write a CSS preprocessor.[/color]

      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.

      Comment

      • Paul McGuire

        #4
        Re: stumped by tricky logic

        "Dave" <davidworley@gm ail.com> wrote in message
        news:1138553712 .522747.250050@ z14g2000cwz.goo glegroups.com.. .[color=blue]
        > 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; }
        > =============== =============== ===========[/color]


        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,Co mbine,Group,alp has,nums,alphan ums,\
        Forward,ZeroOrM ore,cStyleComme nt,ParseResults

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

        # define identifiers, property pattern, valid property values, and property
        list
        ident = Word(alphas,alp hanums+"_")
        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 ).setResultsNam e("propList")

        # define selector - must use Forward since selector is recursive
        selector = Forward()
        selector_conten ts = (prop_list) | Group( dot_ident.setRe sultsName("name ") +

        prop_list ) | selector
        selector << Group( pound_ident.set ResultsName("na me") +
        lbrace +
        Group(ZeroOrMor e(
        selector_conten ts )).setResultsNa me("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.parseS tring(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,e nd in selector.scanSt ring(cssSourceT ext):
        # pprint.pprint(r esults.asList() )

        # a recursive function to print out the names and property lists
        def printSelector(r es,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_selecto r', [['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; }



        Comment

        Working...