telofon: kleiner Parser, Bewertung

Hallo und guten Morgen,

ich habe gerade die erste Vorabversion meines Text-zu-HTML-Parser fertiggestellt. Wie neulich in einem Thread von mir (weiter unten) angesprochen, habe ich folgende Syntax gewählt:

[1=...] bis [4=...] => Überschriften
[link=...] und [link=...@titel=...] => Hyperlinks einfach/komplex
[liste=...,...] => Aufzählungsliste, eine Ebene
[nliste=...,...] => Nummerierte Liste, eine Ebene
[f=...], [k=...] und [u=...] => Fett, Kursiv und unterstrichen

Erst mal meine Frage: Was haltet ihr von dieser Logik? Ist sie einprägsam, einfach zu lesen und zu erlernen? Gibt es Ecken, an denen man noch etwas verbessern kann?

Und zum dieser Code:

  
 function getHTML($text)  
 {  
  $ret=$text; unset($text);  
  
  //[2=...] => <h2>...</h2> usw.  
  $ret=preg_replace("~\[([0-4])=(.+?)\]~", "\t<span class=\"headline-$1\">$2</span><hr><br>\n\n", $ret);  
  //Überschriften für das Inhaltsverzeichnis herausexportieren  
  preg_match_all("~\[([0-4])=(.+?)\]~", $ret, $this->hl_matches);  
  
  //[f=...] => Fett / [k=...] => Kursiv / [u=...] => Unterstrichen  
  $ret=preg_replace("~\[f=(.+?)\]~", "\t<span style=\"font-weight:bold;\">$1</span>", $ret);  
  $ret=preg_replace("~\[k=(.+?)\]~", "\t<span style=\"font-style:italic;\">$1</span>", $ret);  
  $ret=preg_replace("~\[u=(.+?)\]~", "\t<span style=\"text-decoration:underline;\">$1</span>", $ret);  
  
  //[liste=..., ...] => Aufzählungsliste  
  preg_match_all("~\[liste=(.+?)\]~", $ret, $list);  
  $list=explode(",", $list[1][0]);  
  $list_text="\t<ul>\n";  
  foreach($list as $l)  
   $list_text.="\t\t<li>".$l."</li>\n";  
  $list_text.="\t</ul>";  
  $ret=preg_replace("~\[liste=(.+?)\]~", $list_text, $ret);  
  
  //[nliste=..., ...] => Nummerierte Liste  
  preg_match_all("~\[nliste=(.+?)\]~", $ret, $list);  
  $list=explode(",", $list[1][0]);  
  $list_text="\t<ol>\n";  
  foreach($list as $l)  
   $list_text.="\t\t<li>".$l."</li>\n";  
  $list_text.="\t</ol>";  
  $ret=preg_replace("~\[nliste=(.+?)\]~", $list_text, $ret);  
  
  
  //[link=...@title=...] => URL komplex  
  $ret=preg_replace("~\[link=(.+?)@titel=(.+?)\]~", "<a href=\"$1\">$2</a>", $ret);  
  
  //[link=...] => URL einfach  
  $ret=preg_replace("~\[link=(.+?)\]~", "<a href=\"$1\">$1</a>", $ret);  
  
  return $ret;  
 }  

Ist der Code sehr serverlastig? Voraussichtlich werden viele Benutzer (ich habe leider keine genauen Angaben, denke aber keinesfalls mehr als 10 pro Minute) den Code ausführen.
Und zum Programierstil: Gibt es eine elegantere Lösung als diese foreach-Schleifen bei den Listen?

Gruß
telofon

  1. Erst mal meine Frage: Was haltet ihr von dieser Logik?

    Ich halte nicht viel von der Logik immer wieder neue Syntaktische Meisterleistungen zu erfinden.

    Einen Parser zu schreiben, des Experimentierens willen ist ok - aber diesen dann praktisch irgendwo einzusetzen halte ich für etwas unsinnig.

    Es gibt bereits HTML und als Derivat den BB-Code der bereits sehr verbreitet ist. Zum halbwegs vernünftigen Auszeichnen gibts noch WikiSyntax in diversen Formen - allen voran jene von MediaWiki.

    Dass jemand eine der drei genannten Syntaxen beherrscht ist wahrscheinlich, dass jenand noch eine weitere lernen möchte ist eher unwahrscheinlich.

    Mir ist der Sinn bzw. das Hauptmerkmal von Standards klar: jeder hat seinen eigenen - aber das führ zu Formatkriegen und zu Wildwuchs von dem letztlich keiner einen Vorteil zieht.

  2. Damit ich auch noch etaws sinnvolles beisteuere:

    Und zum Programierstil: Gibt es eine elegantere Lösung als diese foreach-Schleifen bei den Listen?

    implode() :)

  3. [1=...] bis [4=...] => Überschriften
    [link=...] und [link=...@titel=...] => Hyperlinks einfach/komplex
    [liste=...,...] => Aufzählungsliste, eine Ebene
    [nliste=...,...] => Nummerierte Liste, eine Ebene
    [f=...], [k=...] und [u=...] => Fett, Kursiv und unterstrichen

    Erst mal meine Frage: Was haltet ihr von dieser Logik? Ist sie einprägsam, einfach zu lesen und zu erlernen? Gibt es Ecken, an denen man noch etwas verbessern kann?

    Versuche nicht, die eher anonymen Elementnamen von HTML nachzuahmen

    [head:]
    [chapter:]
    [legend:]
    sind aussagekräftiger.

    Mache dir in deinem Parser Gedanken zu Verschachtelung:

    <
      [url:>
      [label:]
      [hreflang:]
      [rel:nofollow]
    ]
    [img:
      [url:]
      [alt:]
      [legend:]
      [cite:]
    ]

    Sowie der Frage, welche Optionen sind optional und welches sind Defaultwerte, bzw. wann werden sie gesetzt.

    Und zum dieser Code:

    ... eine Liste vonn Patterns

      
    
    > Ist der Code sehr serverlastig?  
      
    Er ist unübersichtlich, und das Parsen einer Funktion ist abhängig vom bisherigen Parsen.  
    [k=[f=fett und kursiv]]  
    ... geht, aber nicht  
    [f=[k=fett und kursiv]]  
      
    Ich arbeite in Perl wie folgt:  
      
    # Dieses Pattern erlaubt mir, verschachtelten Code zu erkennen.  
    ~~~perl
    my $Nested2Level = qr{  
    	[^\[\]]*  
    	(?:  
    		\[  [^\[\]]*  
    			(?:  \[  [^\[\]]*  \]  [^\[\]]*  )*  
    		\]  [^\[\]]*  
    	)*  
    	}x;  
    # Hauptparser  
    # Erkennt Syntax, aber nicht dir konkrete Funktion  
    sub parse_function{  
        my $t=shift || '';  
        for($t){  
            # \[ist:maskiert\] soll nicht als Funktion verstanden werden  
            # deshalb (?<!\\) \[  
            s{ (?<!\\) \[ ([a-z]+) : ($Nested2Level) (?<!\\) \] }  
             { ehf_function_dispatch( $1, $2 ) }exg;  
            s#\\\[#[#g;  # maskierungen entfernen  
            s#\\\]#]#g;  # maskierungen entfernen  
        }  
        return $t;  
    }  
      
    # Testet ob der Inhalt einer Syntax eine Funktion entspricht.  
    sub ehf_function_dispatch{  
        exists $EhfFunction_Dispatch{$_[0]}  
             and return $EhfFunction_Dispatch{$_[0]}->($_[1]);  
        return('\\[' . $_[0] . ':' . $_[1] . '\\]');  
    }  
      
    # wobei alle Referenzen zu den Callbackfunktionen in eine Hash gespeichert werden:  
      
    my %EhfFunction_Dispatch = (  
    	get          => \&user_get,  
    	footnote     => \&user_footnote,  
    	link         => \&user_link,  
    	mailtolink   => \&user_mailtolink,  
    	mailthispage => \&user_mailthispage,  
    	image        => \&user_image,  
    	navigation   => \&user_navigation,  
    	plugin       => \&user_plugin,  
    	htmlchars    => \&user_htmlchars,  
    	select       => \&user_select,  
    	ml           => \&user_ml,  
    	# Funktionen für Spezialseiteneinbindung  
    	guestbook    => \&user_guestbook,  
    	formmail     => \&user_formmail,  
    	news         => \&user_news,  
    	register     => \&user_register,  
    	search       => \&user_search,  
    );  
      
    #und dann folgen die einzelnen Funktionen:  
    # hier nur stellvertrentend ein:  
      
    sub user_link{  
        my $t = shift;  
        my $url = my $label = '-';  
        my $rel = my $hreflang = '';  
        if( $t =~ m#((https?://[^/\s\[\]]*) (?:( / (?:[^\?\s\#\[\]]*)? ) (?:\?[^\#\s\[\]]*)? )? ) #x ){  
            $url = $1;  
            $label = $2 || '' . $3 || '';  
        }  
        elsif( $t =~ m#\[url:(.*?)\]#x ){  
            $label = $url = $1;  
        }  
        $url =~ m/^__/ and $User{status} eq 'robot' and return '';  
        if( $url and $url !~ m!^(https?:|__)! ){  
            exists $Store->{page}{$url}  
                or return('<b class=warning>ERROR: '.t('Page does not exist').'!</b>');  
            $Userlevel{ $User{status} } < $Userlevel{ $Store->{page}{$url}{status} } and  
                return('<b class=warning>'.t('Rechte nicht ausreichend').'</b>');  
        }  
        $t =~ m/\[label:(.*?)\]/ and $label = $1;  
        $t =~ m/\[menu:(.*?)\s+(.*?)\]/  
            and $label = '<var class="menu">'. $1 . '</var> &#x25b8; <var class="menu">' . $2 .'</var>';  
        $t =~ m/\[rel:([a-z\ ]+?)\]/ and $rel = ' rel="'.$1.'"';  
        $t =~ m/\[hreflang:([a-z][a-z](?:-[A-Z][A-Z])?)\]/ and $hreflang = ' hreflang="'.$1.'"';  
        return( '<a'.($hreflang ? $hreflang : '').' href="'.$url.'" title="'.$url.'"'.($rel ? $rel : '').'>'.$label.'</a>' );  
    }  
    
    

    Durch diese Anordnung kann ich komplexe Funktionen bereitstellen.
    Funktionen können Optionen enthalten, wobei durch die Callback Funktionen sichergestellt ist, dass die Optionen keine bestimmte Reihenfolge haben müssen.
    Ich habe eine einheitliche Syntax.
    Das Parsen ist performant.

    Wenn ich das jetzt mit deinem Konzept vergleiche:
    Bei dir können Funktionen nur zufällig verschachtelt sein.
    [f:] für fett entbehrt sowohl jeder Memnotik wie Konvention.
    Dass du gleich inline-styles setzt, ist eine zusätzliche Sünde.
    Dein Verzicht auf Callbackfunktionen wird bei Listen und Tabellen zu sehr unzuverlässigen Ergebnissen führen.

    Ich halte deinen Ansatz für wenig geeignet. Die Syntax halte ich für ansatzweise geeignet, jedoch ist [1=] wirklich unter aller Sau.

    Schränke deine Syntax so ein, dass sie konsistent und einfach wird.
    Vermeide Fehler jetzt, bevor du sie in alle Ewigkeiten supporten musst.

    mfg Beat

    --
    Woran ich arbeite:
    X-Torah
    Plädoyer für eine alte Mystik
    und Vers-Einteilung
    in der Torah und der Apokalypse
    Beat Stoecklin 2008
    ><o(((°>           ><o(((°>
       <°)))o><                     ><o(((°>o
    Der Valigator leibt diese Fische