CSS-Parser

Code-Stücke können hier veröffentlicht werden.
Antworten
CrackPod
User
Beiträge: 205
Registriert: Freitag 30. Juni 2006, 12:56

Hallo,

da ich mit dem Adressbuch nich zufriden war und auch keine Lust mehr hatte weiterzumachen :wink:, hab ich mich entschlossen etwas zu schreiben, was ich selber auch gebrauchen kann.
Also hab ich mich entschlossen nen CSS-Parser zu schreiben(den Vorschlag hat irgendjemand hier im Forum gebracht, also es nich 100% auf meinem Mist gewaschen :D )
Nunja, bis jetz ist er noch nich wirklich fertig. Soweit bin ich:
Man bekommt ein Dictionary mit dem Tag, das definiert wurde, als key und als Value eine Liste von den Definitionen.

TODO:
  • Fehler ausgeben, wenn ein Semikolon vergessen wurde
  • Warnungen ausgeben, wenn etwas vergessen wurde(z.B. keine Vordergrundfarbe trotz definierter HG-Farbe)
  • GUI
Wenn jemand von euch noch was einfällt, was man machen könnte sagt mir bitte bescheid.

Kritik und Verbessrungsvorschläge sind erwünscht.

Greetz

----------------------------------------------------------------------------
CSS-Parser (0.0.1a):

Code: Alles auswählen

#!/usr/bin/python
#-*- encoding: utf-8 -*-

class CSSParser:

#-------------------------------------------
    def __init__(self, css):

        #Open CSS file
        self.f = open(css, 'r')

        #Read all lines of the file
        self.lines = self.f.read()

#-------------------------------------------
    def getAttributes(self):
        '''
        Fetch all attributes and there related tags and return them as a dict

        :return tagAttributes: dictionary with the tag as key and a list of the
                               tags definitions as value
        '''

        #get all indexes of the char `}`
        indexes = self._getIndexes(self.lines, '}')
        #and the strings between the indexes
        defs = self._getTags(self.lines,indexes)

        tagAttributes = dict()

        for defin in defs:
            #delete all newlines, tabs and leading and following whitespaces
            defin = defin.replace('\n', '').replace('\t', '').strip()
            attributes = list()

            if defin:
                #get a list of the tagdefinitions
                splitedTag = defin.split('{')
                attrs = splitedTag[1].replace('}', '').strip().split(';')

                #add all definitions in a list
                attributes = [attr for attr in attrs if attr]

                #add the values to the dict
                tagAttributes[splitedTag[0].strip()] = attributes

        #return the dict
        return tagAttributes

#-------------------------------------------
    def _getIndexes(self, string, char):
        '''
        Get the indexes of a string by `char`

        :param string: string may containing char
        :param char: character to search for it to get the indexes

        :return indexes: the indexes if found
        '''
        indexes = [0,]

        while True:
            try:
                #try to get the index number
                index = string.index(char, indexes[-1]) + 1

                #if string.index() was succesfull add the index to the list
                indexes.append(index)
            #break if a ValueError is raised
            except ValueError:
                break

        #return the list with the indexes
        return indexes

#-------------------------------------------
    def _getTags(self, string, indexes):
        '''
        Get the tags with there definitions and return them in a list

        :param string: string witch contains the previously gotten indexcharacters
        :param indexes: a list containing the indexes

        :return tags: a list with the tags and there definitions
        '''
        #last index
        lindex = 0
        tags = list()

        for index in indexes:
            #add the tag to the list
            tags.append(string[lindex:index])

            #update the last index
            lindex = index

        #return the Tags
        return tags


##################### Programmtests #######################
def main():
    css = CSSParser('css.css')

    for key, val in css.getAttributes().items():
        print key + ':'
        for val in val:
            print '\t' + val
        print '-'*40

if __name__ == '__main__':
    main()
Noch ne Frage: Macht es Sinn, bei jeder Methode, die Parameter hat, zu überprüfen, ob auch der richtige Datentyp übergeben worden ist und falls nicht einen TypeError zu werfen oder soll ich das lieber lassen?
CrackPod
User
Beiträge: 205
Registriert: Freitag 30. Juni 2006, 12:56

Ich weiß gerade nich weiter.
Ich bin mir nicht sicher, wie ich checken soll, ob ein Semikolon vergessen wurde.
Bei einer Beispielsdatei:

Code: Alles auswählen

body { color:red;}
div {color:blue;background-color:red;}
a {color:green;
        font-weight:bold;}
span {color:yellow
        float:left}
Da wurde jetzt z.B. bei Span zwischen dem Color und dem Float Attribut ein Semikolon vergessen.
Die Ausgabe der jetzigen Version würde so aussehen:

Code: Alles auswählen

body:
	color:red
----------------------------------------
a:
	color:green
	font-weight:bold
----------------------------------------
div:
	color:blue
	background-color:red
----------------------------------------
span:
	color:yellowfloat:left
----------------------------------------
Jetz hab ich mir 2 Möglichkeiten überlegt:
1. Nach Doppelpunkten suchen wenns mehr wie 1 gibt, dann is was faul.
2. Ich mach mir ein Tupel mit allen Attributen und check ob 2 oder mehr vorkommen - stell ich mir aber sehr langsam vor...

Welche von beiden soll ich nehmen? Oder hat jemand ne andere Idee?

Greetzs
BlackJack

Ich würde wahrscheinlich einen "richtigen" Parser schreiben. Also eher was traditionelles mit Lexer und Parser per Hand geschrieben oder eine PyParsing Lösung.

Dein Parser bekommt zum Beispiel auch Probleme wenn geschweifte Klammern vorkommen, die keinen Block kennzeichnen, z.B. in Kommentaren oder literalen Zeichenketten.
CrackPod
User
Beiträge: 205
Registriert: Freitag 30. Juni 2006, 12:56

BlackJack hat geschrieben:Ich würde wahrscheinlich einen "richtigen" Parser schreiben. Also eher was traditionelles mit Lexer und Parser per Hand geschrieben oder eine PyParsing Lösung.
Was ist Lexer?
BlackJack hat geschrieben:z.B. in Kommentaren oder literalen Zeichenketten.
Definiere bitte literale Zeichenketten.
Und wo sollten die denn vorkommen - außer in Kommentaren? Woanders definieren sie ja einen Block.
Außerdem könnt ich ja die Kommentare ignorieren.

Greetz
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

CrackPod hat geschrieben:
BlackJack hat geschrieben:Ich würde wahrscheinlich einen "richtigen" Parser schreiben. Also eher was traditionelles mit Lexer und Parser per Hand geschrieben oder eine PyParsing Lösung.
Was ist Lexer?
Ein Lexer ist ein Stück Code, das einen Eingabestream von Zeichen in eine Liste von Tokens zerlegt. Tokens sind dann jeweils zusammengehörige Zeichen, die eine logische Einheit bilden (z.B. öffnende-Klammer, Bezeichner, Kommentar). Diese Tokens verfüttert man dann an den Parser, der aus den Tokens normalerweise mithilfe einer Grammatik einen Parsebaum erstellt.
CrackPod hat geschrieben:
BlackJack hat geschrieben:z.B. in Kommentaren oder literalen Zeichenketten.
Definiere bitte literale Zeichenketten.
Und wo sollten die denn vorkommen - außer in Kommentaren? Woanders definieren sie ja einen Block.
Literale Strings, wie in http://www.w3.org/TR/REC-CSS2/syndata.html#strings.
CrackPod hat geschrieben: Außerdem könnt ich ja die Kommentare ignorieren.
Ja, könntest du. Es verkompliziert nur die Sache. Außerdem: Momentan rührst du den Inhalt einer Regel nicht an. Willst du diesen richtig parsen, wird es ziemlich aufwändig und du kommst mit deinem split()-Ansatz nicht weiter.
CrackPod
User
Beiträge: 205
Registriert: Freitag 30. Juni 2006, 12:56

birkenfeld hat geschrieben:Ein Lexer ist ein Stück Code, das einen Eingabestream von Zeichen in eine Liste von Tokens zerlegt. Tokens sind dann jeweils zusammengehörige Zeichen, die eine logische Einheit bilden (z.B. öffnende-Klammer, Bezeichner, Kommentar). Diese Tokens verfüttert man dann an den Parser, der aus den Tokens normalerweise mithilfe einer Grammatik einen Parsebaum erstellt.
Und wie stell ich sowas an?
Soll ich RegEx nehmen oder wie?
Gibt es irgendwo Tutorials dazu?
Ich hab zwar mal auf Google gesucht, aber bin nich wirklich fündig geworden...

Greetz
Benutzeravatar
beewee
User
Beiträge: 35
Registriert: Mittwoch 18. Januar 2006, 22:16

Hi,

probier mal das aus, mit Dokumentation. Ich habs zwar noch nie ausprobiert, hört sich aber gut an:
SilverCity is a lexing package, based on Scintilla, that can provide lexical analysis for over 20 programming and markup langauges.

Scripting language bindings currently exist for Python.
BeeWee
BlackJack

Traditionell nimmt man dazu Programme die Lexer und Parser für eine Zielprogrammiersprache aus einer formalen Beschreibung erzeugen. Für C zum Beispiel Lexx und Yacc oder Bison. Die CSS2 Grammatik im Anhand D bei w3c.org beschreibt die Syntax so ähnlich wie man das für diese Programme tun muss:

http://www.w3.org/TR/REC-CSS2/grammar.html

Suchworte zu dem Themenkomplex sind Grammatik, Parser, Lexer, BNF, EBNF, LL(1) oder LARL(1) Parser.
CrackPod
User
Beiträge: 205
Registriert: Freitag 30. Juni 2006, 12:56

Also ich hab mir jetz BNF und EBNF in Wikipedia durchgelesen und soweit verstanden.
zu LL(1) steht da nich wirklich viel und LARL(1) verweist auf LR(1) was ich gar nich versteh :roll:
BlackJack hat geschrieben:Traditionell nimmt man dazu Programme die Lexer und Parser für eine Zielprogrammiersprache aus einer formalen Beschreibung erzeugen. Für C zum Beispiel Lexx und Yacc oder Bison.
Was ist dann das Traditionelle Programm für Python? Oder soll ich mir den Parser-Generator für C schnappen und den dann in Python einbinden?
Irgendwie is das alles ein wenig arg hoch für mich :D
Aber das is mein Ziel, das zu schaffen und dann will ichs auch schaffen :) - Ich brauch nurn bisschen Hilfe von euch.

Danke schonmal.
So far - Greetz CrackPod
Antworten