Suche Informationen über -- Text - lexer - parser --

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
cracki
User
Beiträge: 72
Registriert: Montag 25. Dezember 2006, 05:01

jens hat geschrieben:Könnte man nicht eine Variante bauen, die allgemein Gültig ist?

Ich meine, das man nicht direkt die Regeln "wenn input = xy dann output =xy" festlegt, sondern das nochmals in einer weiteren Ebene.

Also das man einen Parser hat, bei den man die Syntax quasi per config definiert. So könnte man den Parser für verschiedene Markups gleichzeitig nutzten.
genau sowas habe ich ja auch gemacht. der parser ist ein modul mit funktionen und klassen. das grammar ist ein grosses dict mit nonterminal -> grammar zuordnungen und alles ist mit funktionen modelliert. man muss das grammar selbst also nicht nochmal parsen, weils schon code ist.

alles was man dann noch macht, ist: "parse(input, grammar)" und raus kommt ein baum.

ich weiss, PEGs sind relativ starr und scheinbar auch nicht sehr fehlertolerant was den input angeht. und wie man den baum gescheit weiterverarbeitet (oder beim parsen gleich verarbeitet), das wissen fehlt mir noch.
...meh...
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

cracki hat geschrieben:und wie man den baum gescheit weiterverarbeitet (oder beim parsen gleich verarbeitet), das wissen fehlt mir noch.
Wie man generell einen AST verarbeitet oder modellieren sollte um leichten Zugriff zu haben oder speziell von dem PEG?
EnTeQuAk
User
Beiträge: 986
Registriert: Freitag 21. Juli 2006, 15:03
Wohnort: Berlin
Kontaktdaten:

sape hat geschrieben:
EnTeQuAk hat geschrieben: So... so soll das aber nicht erkannt werden... *grml* -- wie kann ich da besser vorgehen -- wie müsste ich über den Text iterieren, um Tokens anzufertigen, um solche Sachen auch "zuferlässig" zu treffen?
Einfach im Lexer den Text spliten:

Code: Alles auswählen

src = """{{{LITERAL_ALL_CHARS
}}}
**//test//**
"""    
regexp = re.compile(r'({{{|}}}|\*\*|//|__|)')
output = [x for x in regexp.split(src) if x != '']
print output

Code: Alles auswählen

['{{{', 'LITERAL_ALL_CHARS\n', '}}}', '\n', '**', '//', 'test', '//', '**', '\n']
Die einzelnen Tokens muss dann der Lexer mit den richtigen Typ versehen.

BTW: Wichtig ist das der ausdruck im re-teil in runden klammern eingeschlossen ist!!

r'({{{|}}}|\*\*|//|__|)' = OK
r'{{{|}}}|\*\*|//|__|' = Nicht OK weil dann sowas bei rauskommt:

Code: Alles auswählen

['LITERAL_ALL_CHARS\n', '\n', 'test', '\n']
lg
Hey... das mit der Klammer wars :D Das werde ich gleich mal ausprobieren.

Herzlichen Dank! :)

MfG EnTeQuAk
cracki
User
Beiträge: 72
Registriert: Montag 25. Dezember 2006, 05:01

sape hat geschrieben:Wie man generell einen AST verarbeitet oder modellieren sollte um leichten Zugriff zu haben oder speziell von dem PEG?
wie ich einen AST so bequem wie moeglich weiterverarbeite. dazu zaehlt z.b. baum stutzen, transformationen, solche geschichten. braucht dich aber nicht kuemmern, weil es geht ja, nur ists mir zu umstaendlich. hab auch das drachenbuch noch nicht gelesen, also...
...meh...
BlackJack

Hier noch ein Versuch einen Parser für die dauCMS Syntax zu schreiben:

http://www.ubuntuusers.de/paste/7241/

Eigentlich habe ich den Parser weggelassen. Es gibt einen `Lexer` und einen `DirectXHTMLWriter` der den Tokenstrom direkt und inkrementell in ein HTML-Dokument überführt. Es gibt also keinen AST, sondern immer nur soviel Informationen im Speicher, wie für die Zerlegung einer einzelnen Eingabezeile und den Stack für offene XML-Tags benötigt wird.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

EDIT:

Jack, wie gewohnt saubere Qualität!! :) -- *thumbs-upp*

Ich werde an meiner Version *trotzdem* weiter basteln :D

@EnTe: Ich würde BlackJacks Variante nehmen für euer vorhaben. Sieht sehr preformant und speicher schonend aus :) Meine wird wohl ein wenig Overkill, da ich gleich einen allgemeinen Markup=>AST=>X-Beliebiges Format-Generator schrieben werde (Bin schon ziemlich weit.), der auch BBC, reStr, etc in ein AST überführen kann. -- Also für euer vorhaben wird es wider zu groß denke ich.

lg
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Hmm, irgendwie verstehe ich nicht so ganz wie dein Lexer den Beginn- und End-Tag von Markups mit gleichen Token unterscheiden kann :? Bei mir musste ich dafür ein par Zeilen schreiben.

EDIT: Ups, hab ein Denkfehler gehabt. Ist für Markups ja garnicht notwendig. Wenn z.B. ein ohne endtag gefunden wurde, wird es ehe als plaintext interpretiert.
BlackJack

sape hat geschrieben:Hmm, irgendwie verstehe ich nicht so ganz wie dein Lexer den Beginn- und End-Tag von Markups mit gleichen Token unterscheiden kann :? Bei mir musste ich dafür ein par Zeilen schreiben.
Das kann mein Lexer auch nicht, das wird im HTML-Writer mit dem Stack gemacht.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Nun hab ichs verstanden, nach dem ich die Scource ein wenig länger durchgelesen habe. (Einige Sachen waren für mich nicht so offensichtlich.)

BTW: Die regexp von dir verstehe ich noch nicht. Müssen die so Kompliziert sein?

Ich hab das so gelöst mit de regexp (Kleiner Ausschnitt):

Code: Alles auswählen

[...]
from ast.ast import MarkupClassNames as MCN

_NAME_OF_THIS_MODULE = os.path.basename(__file__).split('.')[0]


class BasisLexer(object):[...]
    def __init__(self, source):
        self.markup_identifier = (
            # Markups die den gleichen Bezeichner für Begin und Ende verwenden.
            {MCN.MARKUP_BOLD:      (r'\*\*', None)}, # <- **
            {MCN.MARKUP_ITALIC:    (r'//', None)},
            {MCN.MARKUP_UNDERLINE: (r'__', None)},
            # Markups die unterschiedliche Bezeichner für Begin und Ende verwenden.
            {MCN.MARKUP_CODE:      (r'{{{', r'}}}')}
        )
        self.init(source)
        
    def init(self, source):
        mre = re.compile(self._merge_markups_to_restr())
        self.source = [x for x in mre.split(source) if x != '']
        self._lexer_stream = None
        [...]
        
    def _merge_markups_to_restr(self):
        li = list()
        for elem in self.markup_identifier:
            identifier = elem.values()[0]
            if identifier[1] is not None:
                li.append("%s|%s|" % (identifier[0], identifier[1]))
            else:
                li.append("%s|" % identifier[0])
        
        return r"(%s)" % "".join(li)[0:-1]

[...]
Das ergibt (\*\*|//|__|{{{|}}}). Damit splite ich den text. Ist das eventuell nicht ok? Welchen vorteil habe ich mit deinen regexp code? -- Bisher habe ich damit alles richtig gesplitet gekriegt ohne Fehler.

BTW: Falls gleich die fragen kommen wegen `` def init(self, source):``
Ganz Simpel: Jeder Lexer erbt nachher von ``BasisLexer`` und muss
``self.markup_identifier `` in ``__init__`` implementieren und darf nicht ``BasisLexer.__init__(...)`` aufrufen, sondern muss ``self.init(...)`` aufrufen. Den Rest erledigt die Klasse. Eventuell noch ein par Anpassungen (Falls der Lexer nicht genug kann)...
BlackJack

sape hat geschrieben:BTW: Die regexp von dir verstehe ich noch nicht. Müssen die so Kompliziert sein?

Ich hab das so gelöst mit de regexp (Kleiner Ausschnitt):

[…]

Das ergibt (\*\*|//|__|{{{|}}}). Damit splite ich den text. Ist das eventuell nicht ok? Welchen vorteil habe ich mit deinen regexp code? -- Bisher habe ich damit alles richtig gesplitet gekriegt ohne Fehler.
Das ist schon okay, erkennt aber weder Überschriften noch "Befehle mit Argumenten" wie ``#image[spam/eggs.png]``. Den ersten Teil habe ich genauso wie Du.
cracki
User
Beiträge: 72
Registriert: Montag 25. Dezember 2006, 05:01

blackjack:
nur eine frage. erkennt dein code auch geschachtelte tags, auch vom gleichen typ? oder z.b. auch nocode-tags?
...meh...
BlackJack

cracki hat geschrieben:blackjack:
nur eine frage. erkennt dein code auch geschachtelte tags, auch vom gleichen typ? oder z.b. auch nocode-tags?
Geschachtelte Tags ja, aber vom gleichen Typ? Wie soll das gehen und wozu soll das gut sein? Beispiel:

**foo**bar**baz**

Sollen da jetzt `foo` und `baz` fett sein, oder alles fett und `bar` doppelt fett, oder nur einfach fett, oder…?

Das hier: **foo__bar__baz** wird jedenfalls zu foobarbaz

Was sind `nocode`-Tags?
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

BlackJack hat geschrieben: Das ist schon okay, erkennt aber weder Überschriften noch "Befehle mit Argumenten" wie ``#image[spam/eggs.png]``. Den ersten Teil habe ich genauso wie Du.
Das kommt noch. Sind ja nur 3 zusätzliche Einträge in der Tabelle :)
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

BlackJack hat geschrieben: **foo**bar**baz**

Sollen da jetzt `foo` und `baz` fett sein, oder alles fett und `bar` doppelt fett, oder nur einfach fett, oder…?
Ganz simpel: ``foo`` und ``baz`` ist fett und bar nicht. Logisch! ->
**foo**
bar
**baz**

Alles andere wäre auch sehr komisch. Daher sehe ich inmoment auch nicht den Sinn drine.

Cracki, es wird ja immer vom ersten aufkommen von z.B. ``**`` bis zum nächsten aufkommen ausgewertet. Verschachtelungen gleichen Types sind garnicht möglich (Ohne Sonderreglung) und völlig Sinnfrei. Verschachtelungen anderen Typs sind mögcih, da bei BlackJacks Variante ein Stack zum Einsatzt kommt (Bei mir wird sowas schon im Lexer ausgewertet und der Strom dementsprechende angepasst.).


BlackJack ich glaube mit noncode meint er sowas(?):
** {{{ test }}} **

in BBC würde sowas daraus werden (Bold wird zwar ausgewertet (Tags werden nicht angezeigt), wirkt sich aber nicht auf code-tag aus.:

Code: Alles auswählen

 test 
[/b]

Also

Code: Alles auswählen

[b][code] test 
[/b][/code]
Da ist die Frage was EnTe in seiner Spezifikation vorgesehen hat.



Zumindest ist der umgekehrte Fall klar: {{{ ** test ** }}} =

Code: Alles auswählen

[b]test[/b]
Also

Code: Alles auswählen

[b]test[*b][*code] 
[i](*=/)[/i]

Das macht das, was auch EnTe vorgesehen hat.

lg
EnTeQuAk
User
Beiträge: 986
Registriert: Freitag 21. Juli 2006, 15:03
Wohnort: Berlin
Kontaktdaten:

Also Jungz! -- Super Arbeit! :)


Ich bin jetzt erstma ne ganze Woche in Cottbus, zu nem Seminar... leider ohne Lappi. Aber dannach werde ich eure Lösungen sehr gerne und mit viel Gewissen ausprobieren.


Ich wollte jedenfalls schonmal sehr großen Dank an BlackJack und sape und die anderen hier ausrichten! :)


MfG EnTeQuAk
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Hi. Hab nun nach ein wenig rumprobieren und lesen des Atikels bei wikipedia folgende RegExp für Commands. Die fügt sich gut in meinen Lexer ein und ist ziemlich flexibel.

r'(#link)([ \t]*)(\[{1})([^\[\].]+)(]{1})'

Damit sind folgende alle erlaubt:
#link[foobar]
#link [ foobar ]
#link [ssss]]]]] # Weitere ] am ende werden als Literal interpretiert.

Folgendes ist nicht erlaubt:
#link [[foobar]
#link foobar
#link [foobar
# link [foobar]
#link[]

Der Ausdruck ([ \t]*) lest sich z.B. durch ([\s]*) ersetzten und dann wäre es auch Zeilen unabhängig (fast)->

Code: Alles auswählen

#link
[ fobar]
Sowas kriege ich ncoh nicht hin:

Code: Alles auswählen

#link [
fobar
]

oder

#link
[
    foobar
]
Nun die Frage: Wie schnell ist die RegExp? Weil ich hab mir gedacht, da es flexibel ist, ein speziellen Tag in dem Lexer einzurichten, wo man Funktionsname, eine öffnende und schließende Klammer seiner Wahl angeben kann und dann die obere RegExp erzeugt wird :) -- Der Einfachheit halber, da es doch schon ziemlich gedauert (1 Stunde mit Tests) hat bis Ich so eine RegExp hingekriegt habe. Und das will ich nicht unbedingt jemande zumuten, wenn jemand später mein Script nutzt ;)

Ein wenig Info:

# (#link) = #link muss vorkommen
# ([ \t]*) = es dürfen nach #link beliebig viele Leerzeichen und tabs vorkommen
# (\[{1}) = es muss danach ein [ kommen und nur einmal!
# ([^\[\].]+) = es dürfen danach beliebige Zeichen kommen
# (]{1}) = es muss danach ein ] folgen. Danach sind weitere ] erlaubt!
# sie werden lediglich als laterale aufgefasst ;)

Code: Alles auswählen

    source = """
    #link [ fobar]
    
"""
    TAGS_RE = re.compile(r'(#link)([ \t]*)(\[{1})([^\[\].]+)(]{1})')
    tokens = [x for x in TAGS_RE.split(source) if x != '' and x != None]
    print tokens
    # ['\n    ', '#link', ' ', '[', ' fobar', ']', '\n    \n']
Würde mich über ein par Tipps freuen wie man RegExp auf Effizienz hin testet, da ich mit RegExp noch wenig Erfahrung habe.

lg
Benutzeravatar
Mr_Snede
User
Beiträge: 387
Registriert: Sonntag 8. Februar 2004, 16:02
Wohnort: D-Dorf, Bo

Hallo alle zusammen und vielen Dank für eure Hilfe vei unseren Problemen / Ideen zu dauCMS.

Unter:
http://www.daucms.de/dokumentation/index.html
ist zu sehen, was dauCMS im Moment kann. (oder eben auch nicht)

Ich verfolge diesen Thread aufmerksam, denn ich bin dabei Listen als Wikisyntax zu implementieren.
Für das Menü habe ich bis jetzt eine simple Schleife genommen.
Mein Ziel ist es einen Block zu schreiben, der sowohl das Menü generiert (halt eine Liste mit Links) als auch eine normale Liste innerhalb einer Textseite.

Ich schaue mal, wie sich meine Schleife dementsprechend aufbohren lässt. denke aber, dass ich über einer Rekursion recht elegant vorwärts komme. Wobei ich da dann schon wieder bei den Bäumen bin, die zu Anfang des Threads angesprochen wurden.

Code: Alles auswählen

#link [ 
fobar 
] 

oder 

#link 
[ 
    foobar 
]
oder

Code: Alles auswählen

#link 
[ fobar]
Müssen meiner Meinung gar nicht abgedeckt werden.

@ BlackJack:
Herzlichen Dank für deinen Code, an sowas wie den ElementTree habe ich wirklich nicht gedacht. Das was ich von deinem Code bis jetzt verstanden habe sieht schon sehr elegant aus.

Kann es sein, dass es unter Python2.5 (debian etch) den SimpleXMLWriter nicht gibt?

Code: Alles auswählen

mr_snede@pc-etch:~/div$ python2.5 ./dauparser_by_blacbird.py 
Traceback (most recent call last):
  File "./dauparser_by_blacbird.py", line 58, in <module>
    from elementtree.SimpleXMLWriter import XMLWriter
ImportError: No module named elementtree.SimpleXMLWriter
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Hab ein paar Ideen für euer Markup: http://www.daucms.de/dokumentation/markup.html

Die Überschriften mit
= Überschrift =
== Überschrift ==

ist vielleicht noch ok. Wobei ich meine tinyTextile
h1. Überschrift
h2. Überschrift

besser finde, weil kein Endtag nötig ist und näher an HTML ran ist.

Die Links finde ich aber gar nicht gut! Ich meine, das ist ein wenig zu umständlich. OK, meine Links mit "LinxText":http://www.url.de ist auch nicht das wahre. Weil man dahinter keine Satzzeichen machen kann, die super tolle "Webseite":http://www.boa.de!!!
Da würde ich für das einfache moin Linktag pledieren: Ein super [http://www.boa.de Webseite]!!!
Aber warum muß man bei euch noch ein "#link" davor machen? Weil es auch ein "#image" gibt?

Bilder werden bei mit mit !/pics/bild.jpg! eingebunten. Vielleicht auch nicht optimal. Man könnte aber auch einfach [/pics/bild.jpg Ein Bild] machen und "Ein Bild" ist das "title"... Man muß dann allerdings den Link untersuchen, ob es eine URL ist, oder ein Bild. Aber das dürfte nicht schwer werden...

Was ist mit Listen??? Bei mir sehen die so aus:

Code: Alles auswählen

* 1. Eintrag in der erste Ebene
** 1. Unterprunkt in der zweiten Ebene
**** 1. Subunterpunkt in der vierter Ebene
**** 2. Subunterpunkt in der vierter Ebene
** 2. Unterprunkt in der zweiten Ebene


Nummerierte Liste:

# Nummer eins
# Nummer zweite
## und so...
## ...weiter...
Aber auch da könnte man vielleicht besser die moin Syntax nehmen.

Ich habe mir eh überlegt, evtl. mein tinyTextile Marup zu verbessern. Ich hab zwar einen Unitest, aber tinyTextile fällt da an ein paar Stellen durch. Es gibt Probleme bei Escaping/Codeblocks...

Die komplette tinyTextile Syntax: http://pylucid.org/index.py/TinyTextileExample/
Der Unitest: http://pylucid.net/trac/browser/trunk/t ... Textile.py

EDIT: Aber mal was anderes: Ob man nicht pygments dafür her nehmen kann um ein Markup zu schreiben???

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

sape hat geschrieben:Hi. Hab nun nach ein wenig rumprobieren und lesen des Atikels bei wikipedia folgende RegExp für Commands. Die fügt sich gut in meinen Lexer ein und ist ziemlich flexibel.

r'(#link)([ \t]*)(\[{1})([^\[\].]+)(]{1})'
Du machst zu viele unnötige Gruppierungen. Was Du hier am Ende einfach abfragen können möchtest, sind der "Befehl" (link), sofern es später verschiedene Befehle geben soll, und alles was zwischen den eckigen Klammern steht. Und den Gruppen würde ich dann auch Namen verpassen, das macht den Zugriff einfacher und das Programm verständlicher.

Die "Mengenangabe" ``{1}`` kannst Du weglassen. Der Ausdruck "x" sagt "ein `x`", der Ausdruck "x{1}" sagt "ein `x` nur einmal". Das ist redundant.

Und bei ``([^\[\].]+)`` bin ich mir nicht so ganz sicher warum Du ``[`` verbietest und ziemlich sicher das Du eigentlich keine Punkte verbieten willst.
BlackJack

Mr_Snede hat geschrieben:Kann es sein, dass es unter Python2.5 (debian etch) den SimpleXMLWriter nicht gibt?
Wenn man ihn nicht installiert, dann nicht. :-)
Antworten