... dass beispielsweise die diversen Wikiengines die Umformatierung von spezifischem Markup in HTML mit Regexen erledigen, wo eine BaumTransformation geeigneter wäre.
Was ist gemeint? Eine Wiki-Engine ist ein Compiler. (Ja, wirklich. Sie hat eine Eingabesprache und übersetzt in eine Ausgabesprache, also ist sie ein Compiler.) Compiler haben sinnvollerweise drei Schritte auszuführen:
Resultat solcher Abkürzungen ist, dass manches eigentlich korrektes Markup in ungültiges HTML übersetzt wird. Schlimmer noch, die Wiki-Engine merkt nicht, dass sie versagt hat. [Natürlich hängen an einer Wiki-Engine keine Menschenleben und das ist vielleicht wirklich nicht so schlimm. Allerdings wird mir schlecht, wenn ich dran denke, dass diese Sorte Programmierer auch Bezahlsysteme in Webshops einbindet.]
Beispiel für kaputtes HTML:
''Hervorgehobener Text mit '''fettem Einschub, der wieder ''hervorgehobenen'' Text''' enthält.''Das muss nicht so sein. Es gibt zivilisiertere Sprachen.
Wie macht man es richtig? Man schreibt einen Parser für Wiki-Markup, transformiert den Parse-Baum, schickt ihn dann durch den Un-Parser für HTML. Eine geeignete Sprache vorausgesetzt, ist der meiste Code trivial und die explizite Repräsentation der Bäume kann sogar weggelassen werden. Lohnt sich aber eigentlich nicht, die Syntaxbäume sind nützlich, um beispielsweise Inhaltsverzeichnisse zu generieren. Als Nebeneffekt ist auch noch korrektes HTML garantiert.
Beispiel in Haskell. Hier wird von stdin geparst und gleich wieder Text auf stdout erzeugt, das aber nur, um den Code kurz zu halten. Dieser Parser bearbeitet Fettschrift, Kursivschrift und Wikilinks, und es ist leicht zu beweisen, dass seine Ausgabe unter allen Umständen korrekt geschachtelte Tags besitzt.
import Control.Monad import Data.Char import Text.ParserCombinators?.ReadP?main = getContents >>= putStrLn . wikiToHtml wikiToHtml = fst . head . filter (null.snd) . readP_to_S wiki_p
wiki_p = rep $ bold_p +++ italics_p <++ text_p italics_p = enclose "em" `liftM` inside two_apos (rep $ bold_p <++ text_p) bold_p = enclose "strong" `liftM` inside (string "'''") (rep $ italics_p <++ text_p)
-- Die folgende Zeile ist eigentlich ein Regex. Geht offensichtlich mit -- Parserkombinatoren genauso gut. text_p = do kw <- rep2 (upper .++. rep1 lower) return $ concat ["<a href=\"",kw,"\">",kw,"</a>"] <++ do single_apo <++ do escape `liftM` satisfy (/= '\'')
-- Die Behandlung einzelner und doppelter Hochkommata ist unsinnig schwierig. -- Literale Hochkomma gibt's nur wenn sie wirklich einzeln stehen. Doppelte leiten -- Kursivschrift ein, auch dann, wenn vier oder mehr beieinander stehen, nicht -- aber, wenn es drei sind. Klingt kompliziert, geht aber mit Lookahead noch -- ganz gut. single_apo = do satisfy (== '\'') rest <- look case rest of '\'':_ -> mzero _ -> return "\'"
two_apos = do string "''" rest <- look guard $ case map (== '\'') rest of (True:False:_) -> False [True] -> False _ -> True
-- ab hier einige Utility-Funktionen. In einem größeren Programm gehören die ggf. -- in eine separate Bibliothek. inside sep p = do sep ; s <- p ; sep ; return s
enclose tag str = concat ["<",tag,">",str,"</",tag,">"]
escape c | c >= ' ' && c <= 'z' = [c] | otherwise = "&#" ++ show (ord c) ++ ";"
(.++.) = liftM2 (++) rep p = (p .++. rep p) +++ return [] rep1 p = p .++. rep p rep2 p = p .++. rep1 p lower = (:[]) `liftM` satisfy isLower upper = (:[]) `liftM` satisfy isLower
Ich habe mich über dies und ähnliches schon oft geärgert. Das Problem ist natürlich nicht auf Wikis oder gar auf Perl beschränkt und die Diskussion hier sicher falsch aufgehoben. Beispiele:
Nicht "signiert" von SDö ;-)
Nicht ganz. Der Mensch muss definieren, wie die Maschine mit dem Text umgeht. Wenn die Definition Verschachtelungen zulassen soll, sind Regexe eben genau nicht das Mittel der Wahl. Und schließlich sind "99% korrekt" eben immernoch inkorrekt. Das bekommt man dann zu spüren, wenn in dem letzten Prozent eine Sicherheitslücke steckt. Hat die 99% überhaupt jemand gemessen? Mich überrascht Wikis Verhalten eigentlich jedesmal wieder...
Zu: "Der Mensch muss definieren [...]": Schon klar, aber ist eine komplizierte, vollständige Definition notwenigerweise einer einfachen, unvollständigen vorzuziehen?
Wer sagt denn, dass die korrekte Definition kompliziert ist? Was der Benutzer über das Verschachtelungsverhalten im Kopf hat, ist ja gerade eine kontextfreie Grammatik. Die operationale Semantik "Wiki-Markup ist, was meine Regexe daraus machen." ist im Vergleich dazu viel komplizierter.
Also mal ein Beispiel für "99% korrekt". Auf bremen.de kann man einen interaktiven Stadtplan aufrufen. Nur leider kann man nach dem Verkleinern den Ausschnitt nicht mehr nach rechts verschieben. Problem? In der URL werden mit regulären Ausdrücken alte durch neue Werte ersetzt. Initialisiert wird der Wert zf mit 1, der reguläre Ausdruck erwartet aber irgendwas mit Komma und frisst deshalb das folgende Und-Zeichen. Dadurch fehlt das Und vor dem ox.
http://bremen.de/sixcms/detail.php?template=01_stadtplan_start&id=636342
Hier eine fehlerhaft erzeugte URL: http://bremen.de/sixcms/detail.php?template=01_karte2_d&ai=2091819&qf=60&zf=0.8ox=300&oy=0
und so wär's richtig: http://bremen.de/sixcms/detail.php?template=01_karte2_d&ai=2091819&qf=60&zf=0.8&ox=300&oy=0
Nun kommen natürlich die Skriptsprachen-Spezialisten und sagen, in dieser Anwendung würden einem kompliziertere Grammatiken auch nicht helfen und Programmierfehler können einem ja mit jedem System passieren. Na ja, was reg ich mich auf.
Außerdem würde mich mal interessieren, wieso auf dieser Wiki am Ende einer Vorschau immer noch einmal der Text unformatiert angehängt wird.