ZBNF-Beschreibung

ZBNF-Beschreibung

Inhalt


Topic=.ZBNF_syntaxDescription.

Das BNF, Backus-Naur-Format -->Wikipedia ist enstanden mit der Entwicklung von Algol anfangs der 60-ger Jahre und stellte einen wichtigen Meilenstein in der Entwicklung der Softwaretechnologie dar. Mit BNF war es das erste mal möglich, die Syntax von Programmanweisungen exakt zu beschreiben.

BNF hat eine vielfältige Weiterentwicklung genommen, ausschlagsgebend war es unter anderem für die Entwicklung der Programmiersprache PASCAL in den 80-gern in der Form -->Wikipedia EBNF. Bekannt ist die Syntaxschreibweise ähnlich BNF beispielsweise in der Angabe der Syntax von Kommandozeilenparamtern, am bekanntesten sind dabei die Optionsklammern [...] und in diesem Sinne gängige Praxis.

Das BNF ist nicht eindeutig standardisiert, es sind verschiedene Varianten in Gebrauch. Dieses Format wird auch eher zur dokumentatischen Beschreibung von Syntax verwendet. Kursivschrift und ähnliche Auszeichnungen in textuellen Beschreibungen sind zwar gut leserlich für die Dokumentation einer Syntax, aber kaum automatisiert verwertbar. In BNF fehlt der semantische Aspekt. Die Semantik wird zumeist anhand der eindeutig beschriebenen Syntax verbal erläutert. Für die automatisierte Verarbeitung ist aber insbesondere die Semantik wichtig, um zu erkennen, was eine Information (eine Zahl, eine Zeichenkette) in einem bestimmten Kontext bedeutet.

Die ZBNF erweitert die BNF um den semantischen Aspekt. Die Syntax der ZBNF ist um einige Aspekete gegenüber EBNF erweitert.

Die Stärke der ZBNF besteht letztlich darin, automatisiert verarbeitet zu werden, und zwar sowohl für das Parsen von Texten im freiem (aber syntaktisch beschreibbaren) Format für die Verarbeitung innerhalb eines Java - Programmes, als auch zur Konvertierung solcher frei formatierter Texte nach XML, um die damit gewonnenen Informationen der vielfältigen Möglichkeiten der Behandlung in XML zuzuführen.

1 Grundform einer Syntaxdefinition in ZBNF

Topic=.ZBNF_syntaxDescription..

Ein ZBNF-Script kann entweder in einem ASCII-File vorliegen oder als String in einem Java-Programm vorgegeben werden. Ein ZBNF-Script kann optional aus einem Kopf bestehen, der auch die Zeichencodierung (encoding) angibt. Es gibt Steuervariable, mit $ beginnend, die bestimmte Einstellungen ermöglichen.

Eine einzelne Syntaxdefinition kann wie folgt dargestellt werden:

syntaxident::=syntaxprescript.

Dabei ist syntaxident der Bezeichner, der als Symbol für die definierte Syntax in anderen Syntaxvorschriften verwendet werden kann. syntaxprescript ist die gesamte Syntaxvorschrift. Der Punkt bezeichnet das Ende.

In ZBNF selbst geschrieben würde die oben angegeben Syntax wie folgt aussehen:

Syntaxdefinition::=<$?syntaxident>::=<syntaxprescript>\..

Das bedeutet: Eine Syntaxdefinition besteht aus einem Bezeichner, der semantisch als syntaxident bezeichnet wird, gefolgt ohne Leerzeichen von 3 zusammenhängen Zeichen ::=. Darauf folgt ein komplexer Ausdruck, der mit dem Bezeichner und gleichzeitig der Semantik syntaxprescript an anderer Stelle definiert ist. Der Abschluss bildet das Zeichen ., notiert mit \. Der darauf folgenden nochmalige einzelne Punkt kennzeichnet in ZBNF das Ende der Syntaxdefinition.

In den folgenden Darstellungen der ZBNF-Syntax wird die ZBNF-Schreibweise verwendet. Zusätzlich sind aber Beispiele angegeben.

Die erste Syntaxdefinition im ZBNF-Script ist der main-Ausdruck, gültig für den gesamten zu parsenden Text. Weitere Syntaxdefinitionen können Details beschreiben. Als Syntaxdefinition wird immer eine gesamte Definition einer (Teil)-Syntax mit zugehörigem syntaxident bezeichnet, unter Syntaxvorschrift oder syntax-prescript wird dagegen auch einen Ausschnitt der eigentlichen Syntaxdefinition verstanden.

Ein Beispiel für ein einfaches ZBNF-Script ist folgendes:

 <?ZBNF-www.vishia.org version="1.0" encoding="iso-8859-1"?>
 $setLinemode.
 einkaufsliste::=Einkaufszettel \\n
 <![=]*?> \\n
 { <position> \\n }.
 position::=<#?@menge> [<?@einheit>Stck|x|] <$?text()>.

In dieser Datei wird zu Anfang die Codierung und danach als Wert der "Zeilenmodus" festgelegt. Der erste ZBNF-Ausdruck "einkaufsliste" ist der main-Ausdruck, mit ihm beginnt das Parsen. Der zweite Ausdruck "position" ist eine Syntaxkomponente, die intern aufgerufen wird.

2 Der sematische Aspekt

Topic=.ZBNF_syntaxDescription.semanticAspect.

Das "Z" in "ZBNF" steht als umgedrehtes "S" für Semantik. Die Semantik kommt in der orignalen BNF, aber auch in EBNF und anderen Varianten zu kurz. Schreibt man beispielsweise:

 variabledefinition::= <identifier> <identifier> ; .
 identifier ::= alphachar [ digit | identifier].
 alphachar = A|B|C|...

dann ist hiermit die Syntax exakt bis ins kleinste definiert. Dass der erste identifier aber eine Typangabe darstellt und der zweite identifier der Name der Variable ist, muss man verbal erklären. Nicht anders ist es bei einer oft üblichen syntaktischen Erklärung von Kommandozeilen-Aufrufen:

 XCOPY Quelle [Ziel] [/A | /M] [/D[:Datum]] [/P] [/S [/E]] [/V] [/W]
                     [/C] [/I] [/Q] [/F] [/L] [/G] [/H] [/R] [/T] [/U]
                     [/K] [/N] [/O] [/X] [/Y] [/-Y] [/Z]
                     [/EXCLUDE:Datei1[+Datei2][+Datei3]...]
   Quelle    Die zu kopierenden Dateien.
   Ziel      Position und/oder Name der neuen Dateien.
   /A        Kopiert nur Dateien mit gesetztem Archivattribut,
             ändert das Attribut nicht.
   /M        Kopiert nur Dateien mit gesetztem Archivattribut,
             setzt das Attribut nach dem Kopieren zurück.

Das Beispiel ist der Anfang des originale Inhalts, wenn unter Windows help xcopy aufgerufen wird. Nicht immer sind die Erklärungen eindeutig. Aber dank der BNF-Schreibweise ist hier beispielsweise genau erkennbar, dass sich die Angaben /A und /M einander auschließen.

Für eine Umsetzung von Texten in Informationsverarbeitung sind aber verbale Erklärungen wenig nutzbringend, notwendig ist meist eine nicht unkomplexe Programmierung der Weiterverarbeitung nach dem Parsen.

Der Gedanke der Verbindung von Information und dessen Bedeutung ist eine der Grundideen von XML. In XML ist die Bedeutung einer Information in den <tagnamen> der XML-Elemente und XML-Attribute angegeben, die Informationen stehen dazwischen <name>Information<subtag>...subInfo</subtag></name> oder als wert von Attributen <name attribute="wert">. Mit dieser Idee ist es nun viel besser maschinentechnisch möglich, Informationen auch dann einer textuellen Darstellung zu entnehmen, wenn es sich um ältere Varianten, Zulieferungen mit etwas anderen Inhalten oder dergleichen handelt. Informationen sind also viel besser kompatibel zu halten.

Der Gedanke der Bindung einer Semantik an eine syntaktische bestimmte Angabe ist nun Kernbestandteil der ZBNF. Damit ist es beispielsweise mögich, ohne zusätzlichen Aufwand aus einem beliebigen syntaktisch fassbaren Text und ZBNF XML zu erzeugen und die weitere Informationsverarbeitung mit den bekannten XML-Mitteln fortzusetzen:

  Text x ZBNF =: XML

Die Umkehrung

  XML  x XSLT =: Text

ist mit Anwendung der XSLT-Technik bereits in XML definiert und gebräuchlich. Das x soll hier als Verknüpfung oder Kreuzprodukt gelesen werden.

Weitere Ausführungen dazu siehe Kapitel Semantikangaben.

3 Terminalsymbole

Topic=.ZBNF_syntaxDescription..

Terminalsymbole sind diejenigen Zeichen der Syntaxvorschrift, die genauso erwartet werden wie vorgeschrieben. Das sind die Schlüsselwörter und -zeichen der Texterkennung. In der BNF gibt es die Variante, bei der die Terminalsymbole in Anführungszeichen geschrieben werden. Das ist in ZBNF nicht so, Terminalsymbole werden direkt notiert. Der Konflikt mit denjenigen Zeichen [ ] | { } < > ? . die für die Syntaxsteuerung selbst benötigt werden, wird mit einem \ als Umschaltzeichen gelöst. Wird beispielsweise die [ als Terminalsymbol benötigt, ist also \[ zu schreiben.

3.1 Spezialzeichen

Topic=.ZBNF_syntaxDescription...

Das \ ist wie in Stringliteralen in Java und C/C++ üblich auch Einleitungszeichen für die Steuerzeichen \n \r \b \t \f mit der Bedeutung Newline (0x0a), Carrige Return (0x0d), Backspace (0x08), Tabulator (0x09), Formfeed (0x0c). Diese Zeichen außer Backspace dienen häufig zur Gliederung von formatgebundenen Texten und sind daher wichtig als Terminalsymbole. Der Backslash selbst wird mit \\ codiert. Die Folge \s steht für ein Whitespace-Zeichen in der Zeile, aber nicht für einen Zeilenumbruch (wie in Regular Expressions unter Unix). Ein \ gefolgt von einem Leerzeichen steht für ein einzelnes Leerzeichen als Terminalsymbol. Die Folge \e bedeutet Textende. Die Syntax ist also passend, wenn der Eingabetext genau an dieser Stelle zu ende ist.

Beliebige Unicode-Zeichen werden mit \uxxxx codiert, wobei xxxx die 4-stellige Hexa-Codierung der UFT16-Tabelle darstellt. Diese Art der Darstellung ermöglicht, Zeichen außerhalb der angegebenen oder eingestellten Zeichencodierung zu codieren.

3.2 Zeichencodierung (Encoding) des ZBNF-Scripts bezogen auf Terminalsymbole

Topic=.ZBNF_syntaxDescription...

Man muss ansonsten darauf achten, welche Zeichencodierung der Editor für einen Textfile verwendet. Das ist beispielsweise unter Windows standardgemäß der 8-Bit-Zeichensatz ISO-8859-1, aber auch UTF-8 ist gegebenenfalls eingestellt ohne dass man das genau weiß. Die Einstellung der Zeichencodierung im abgelegten ZBNF-File ist entweder dort im Kopf selbst notiert und muss auch stimmen, oder sie muss mit der angegebenen Codierung beim Aufruf des Parsers übereinstimmen. Schreibt man Müßiggang als Terminalsymnbol, stimmt aber die Codierung nicht, so werden die Umlaute und ß nicht richtig umgesetzt. Andererseits kann man mit einem entsprechenden Editor unter Angabe der Encoding auch beispielsweise kyrillische oder griechische Terminalsymbole erzeugen.

4 Whitespaces und Kommentare

Topic=.ZBNF_syntaxDescription..

Als Whitespaces werden alle Zeichen in einem Text bezeichnet, die im Ausdruck einen Leerraum produzieren.

4.1 Whitespaces in der Syntaxvorschrift lassen Whitespaces im zu parsenden Text zu

Topic=.ZBNF_syntaxDescription...

In der Synaxdefinition selbst dürfen in jedem Fall Whitespaces verwendet werden: Zwischen den Syntaxelementen können beliebige Leerzeichen oder Zeilenumbrüche stehen. Im Normalfall bedeutet aber ein Whitespace in der Syntaxdefinition, dass auch im zu parsenden Text an dieser Stelle ein Whitespace stehen darf.

4.2 Abschalten dieses Verhaltens mit <$NoWhiteSpaces>

Topic=.ZBNF_syntaxDescription...

Am Anfang einer Syntaxvorschrift kann mit <$NoWhiteSpaces> angegeben werden, dass Whitespaces in der betreffenden Syntaxvorschrift nicht das Zulassen von Whitespaces im Eingabetext bewirken. Exakt ZBNF-Syntaktisch ist das wie folgt beschreibbar:

 Syntaxdefinition::=<$?syntaxident>::=[\<\?<$semanicDerSyntaxdefinition>\>][\<\$NoWhiteSpaces\><?dannKeineWhitespaces>]  <syntaxprescript>\..

Eine Anwendung dessen ist beispielsweise beim Parsen eines #define ...-Ausdruckes in einem C-Header zu finden:

 defineDefinition::=<$NoWhiteSpaces> <$?@name> [ ( { <$?parameter/@name> ? , } ) ]
                    <![ \t]*?> [ <#-?intvalue>
                               | 0x<#x?hexvalue>
                               | <""?stringvalue>
                               |]
                    <![ \t]*?>
                    { <*|\n|\\|\r\n?value>
                    ? \\[\r]\n
                    }.

Bei dieser parsing-Aktion sind Leerzeichen nicht zu überlesen, sondern als Bestandteil der Ausdrücke zu betrachten. Normalerweise werden aber whitespaces im Eingabetext an den Stellen überlesen, an der in der Syntaxvorschrift ein Whitespace steht. Das ist für die meisten Fälle recht praktisch, In diesem Fall müsste man die Syntax ganz ohne Whitespaces schreiben. Der Ausdruck wäre ohne die umgebrochene Schreibweise aber noch etwas weniger verständlich. Umbrechen (das ist ein Whitespace) darf man, da am Anfang <$NoWhiteSpaces> steht, also das Standardverhalten abgeschaltet wird.

Kurze Erklärung der Syntax: In C und C++ ist es so, dass ein #define über eine Zeile verlängert werden kann, wenn hinten ein \ steht. Das muss in ZBNF ausgedrückt werden. Der Umbruch gehört aber nicht mit zum eigentlichen Inhalt des defines, muss also beim Parsen entfernt werden. Die restlichen Leerzeichen sollen zum Inhalt gehören. Der Ausdruck ist so aufgebaut, dass zunächst ein regular expression codiert mit <![ ]*?> am Anfang Leerzeichen oder Tabulatoren überliest. Danach wird versucht, ein intvalue, hexvalue oder stringvalue zu parsen. Das ist für viele Fälle hinreichend und praktisch. Hier ist aber die leere Option möglich, falls der Ausdruck komplexer ist. Danach werden nochmals Whitespaces mit dem regular Expression <![ ]*?> bis ausschließlich dem Zeilenende überlesen. Weitere Zeichen bis zu einem Zeilenende codiert mit oder (Hexa 0x0d, 0x0a) oder Backslash codiert mit \\ werden dann als value gespeichert. Falls danach ein \ unmittelbar gefolgt von einem Zeilenumbruch folgt (Syntax-Schreibweise { ... ? \[ ] }), wird wiederholt und alle weiteren Zeilen komplett als eigener value eingelesen..

4.3 ##Kommentare

Topic=.ZBNF_syntaxDescription...

Im gesamten ZBNF-Script ist ist eine Kommentierung möglich. Kommentare werden mit zwei aufeinanderfolgenden Zeichen ## außerhalb einer Angabe in < > und außerhalb der Umschreibung mit \# eingeleitet und gelten bis zum Zeilenende. Ein einzelnes # wird dagegen als Terminalsymbol gewertet. Möchte man zwei ## als Terminalsymbol haben, dann muss man \#\# schreiben.

4.4 Whitespace- und Kommentar-Verarbeitung des Input-Textes beim Parsen

Topic=.ZBNF_syntaxDescription...

Es gilt die Regel: Wenn in der Syntaxvorschrift Whitespaces angegeben sind, dann dürfen im zu parsenden Text an dieser Stelle ebenfalls Whitespaces auftauchen. Dabei umfassen Whitespaces wie in Programmiersprachen C, C++, Java und viele andere auch Kommentare.

Das genaue Verhalten, was Whitespaces sind und wie Kommentare geschrieben werden und wie sie gegebenenfalls doch erkannt, also geparst und nicht ignoriert werden, lässt sich aber detailiert festlegen. Es gelten folgende Regeln:

  • Mit der Steuervariable $Whitespaces=<*.?Whitespaces>. an beliebiger Stelle im gesamten ZBNF-Script (empfohlen am Anfang) lässt sich festlegen, welche Zeichen als Whitespaces akzeptiert werden. Standardmäßig ist das \ \t\r\n. Beispielsweise kann damit das Zeichen \t ausgeschlossen werden, weil es Sonderbedeutungen hat, indem $Whitespaces=\ \r\n. notiert wird.

  • Standardgemäß werden Kommentare in /* bis */ sowie als Zeilenkommentare nach // erkannt.

  • Die Kommentarzeichen lassen sich um- und abschalten, und zwar mittels folgender Angaben, die an beliebiger Stelle geschrieben (zu empfehlen am Anfang) für alle Syntaxdefinitionen eines ZBNF-Scripts gelten:

  • Mit der Steuervariable $comment=<*|\.\.\.?startCommentString>\.\.\.<*\.?endCommentString>. an beliebiger Stelle im gesamten ZBNF-Script (empfohlen am Anfang) lässt sich festlegen, welche Zeichen als Einleitung und Ausleitung von Comment-Strings gelten. Die Standardfestlegung würde (nicht notwendigerweise) mit $CommentString=/*...*/. angegeben. Sollen beispielsweise Kommentare in (?...?) geschrieben werden, dann ist in diesem Fall zu notieren $comment=(?...?).

  • Mit der Steuervariable $endlineComment=<*.?$endlineComment>. an beliebiger Stelle im gesamten ZBNF-Script (empfohlen am Anfang) lässt sich festlegen, welche Zeichen als Einleitung für Zeilenkommentare gelten. Die Standardfestlegung würde (nicht notwendigerweise) mit $endlineComment=//. angegeben, Eine Angabe $endlineComment=#. schreibt ein # als Zeilenkommentierungs-Einleitungszeichen vor.

  • Mit der Steuervariable $setLineMode. an beliebiger Stelle im gesamten ZBNF-Script (empfohlen am Anfang) lässt sich festlegen, dass die Zeilenumbruchszeichen \n und \r nicht als Whitespaces gelten. Also darf der Anwender zwar Whitespaces schreiben, aber dabei keine Zeilenumbrüche.

  • Beim Parsen wird immer zuerst getestet, ob der Input mit der Syntaxvorschrift übereinstimmt, bevor Kommentare überlesen werden. Man hat damit die Möglichkeit, in der Syntaxvorschrift explizit Kommentare erkennen zu wollen, die sonst überlesen werden würden. Nur wenn keine Übereinstimmung festgestellt wird, wird der anstehende Input als Kommentar überlesen. Damit ist es möglich, das bestimmte Informationen, die in den Kommentaren stehen, dennoch erkannt werden, obwohl Kommentare im allgemeinen überlesen werden. Beispiel dafür ist die Erkennung von Javadoc-like Informationen.

  • Es soll nochmals daran erinnert werden, dass einzelne Zeichen explizit als Terminalzeichen angebbar sind wie \ , \n oder auch [\]\n als Kombination von 0x0d 0x0a oder nur 0x0a. Ein \s als Terminalzeichen testet die Whitespaces wie sie bei $Whitespaces=... angegeben wurden, aber ohne die Zeilenumbruchszeichen.

    4.5 Beispiele zu Terminalsymbolen und Whitespaces

    Topic=.ZBNF_syntaxDescription...

    ZBNF-Schreibweise

    Erläuterung

    Eine Zahl = <#>

    Es wird die Zeichenfolge Eine, gefolgt von Zahl, gefolgt von einem = erwartet. Zwischen diesen Symbolen dürfen Whitespaces stehen, auch Zeilenumbrüche, wenn nicht $setLinemode gesetzt ist. <#> steht syntaktisch für eine positive Ganzzahl.

    Eine\ Zahl\ = <#>

    Es wird eine Zeichenfolge Eine Zahl = erwartet, und zwar mit genau einem Leerzeichen dazwischen. Danach muss die Zahl folgen, und zwar mit einem Abstand von diesmal beliebig vielen Whitespaces, auch keinem Zwischenraum, weil zwischen = und <#> eben nicht \ steht sondern nur ein Leerzeichen steht.

    Eine\sZahl\s=\s<#>\s\n

    Hier wird Whitespace als syntaktisches Element vorgeschrieben. Eine Zeilenumbruch darf aber nicht vorkommen. Nach der Zahl muss aber, nach möglichen Whitespaces, ein Umbruch kommen. Das ist unabhängig von der Einstellung $setLinemode.

    \[ <#> \]

    Hier muss eine Zahl zwischen [ ] stehen.

    5 Syntaktische Konstrukte

    Topic=.ZBNF_syntaxDescription..

    .

    5.1 Alternative ...|...

    Topic=.ZBNF_syntaxDescription...

    Die Alternative war das einzige Syntax-Steuerzeichen der alten BNF aus den 60-ger Jahren. Diese baute nur auf Alternativen und die Rekursion auf. Die Alternative ist kaum mehr nötig um zu definieren:

     ZifferUngleichNull::=1|2|3|4|5|6|7|8|9.
     Ziffer::=0 | <ZifferUngleichNull>.
     Ziffernfolge::= <Ziffer> | <Ziffer><Ziffernfolge>.
     PositiveZahl::= <ZifferUngleichNull><Ziffernfolge>.
    

    wie in Lehrbeispielen oft angebracht wird. Diese Aufgabe wird bei der Konvertierung einer Zahl mit einem fest programmierten Algorithmus wesentlich schneller und letztlich einfacher erledigt (if(digit >'0' && digit <='9')). In der ZBNF stehen für diese Abfragen spezielle Syntaxkomponenten wie <#?Zahl>, <$?Bezeichner> zur Verfügung, die direkt ausprogrammiert sind.

    Die Alternative ist für höhere Dinge notwendig, wie beispielsweise:

     Kunde::=<Privatkunde>|<Firmenkunde>.
    

    wobei sich hinter der Definition von "Privatekunde" und "Firmenkunde" eine komplexe Syntax, die eigenständig definiert ist, verbirgt. Auch mit Terminalsymbolen ist der Aufbau von Alternativen sinnvoll:

     Anrede::= Herr | Frau | Fräulein | Mr\\. | Mrs\\. .
    

    Der antiquieren Ausdruck "Fräulein" ist nur für das Beispiel gedacht. Der Punkt hinter Mr und Mrs muss mit einem \ eingeleitet werden.

    Jede Syntaxvorschrift (rechte Seite einer Syntaxdefinition) oder Teil-Syntaxvorschrift (innerhalb beispielsweise Optionsklammern) kann aus Alternativen bestehen.

    5.2 Optionen [...]

    Topic=.ZBNF_syntaxDescription...

    Einfache Optionen werden wie in der BNF üblich mit [ ] geklammert. Dabei darf das in Klammern stehende vorkommen oder nicht. Diese Schreibweise ist auch außerhalb BNF sehr gebräuchlich.

    Interessant ist, dass Optionen dieser Art auch mitten zwischen Terminalsymbolen vorkommen können. Beispielsweise gab es einen Reportfile, in dem das Wort Telegramm ohne zweites 'e' geschrieben wurde: Telgramm. In einer späteren Version war das 'e' da. Offensichtlich ein Verschreiber, der später korrigiert wurde. Um beide Varianten zu parsen, wurde in der Syntaxvorschrift Tel[e]gramm verlangt. Das sind genau genommen drei verschiedene syntaktische Elemente: Das Terminalsymbol Tel, eine Option, innen mit e als Terminalsymbol, und das Terminalsymbol gramm.

    Innerhalb der Optionsklammern kann eine beliebige Syntaxvorschrift stehen. Der gesamten Syntaxvorschrift kann eine eigene Semantik gegeben werden, indem folgendes geschrieben wird: [<?semantik> syntaxprescript ].

    5.2.1 Auswahl-Option [ ... | ... | ... ]

    Topic=.ZBNF_syntaxDescription....

    Die Auswahl-Option ist eine Kombination von Auswahl mit | und der Optionsklammerung. In der Praxis kommt es häufig vor, das eine Alternative genutzt werden soll. Daher reagiert die ZBNF hier so, dass genau eine dieser Alternativen passen muss. Die Optionsklammerung [ ] bedeutet also dann nicht mehr kann man oder auch nicht sondern eines davon muss sein. Die Optionsklammern [ ] sind dann eigentlich nur noch Klammern um Alternativen. Das kann man benutzen, um semantische Konstruktionen zu bauen wie beispielsweise

     Beispiel::= Alternative1 | Alternative2 | [<?MehrereZusammengefasst> Alternative3a | Alternative3b ].
    

    5.2.2 Auswahl-Option mit leerer Alternative [ ... | ... |]

    Topic=.ZBNF_syntaxDescription....

    Wird als lezte Angabe eine leere Alternative (geschrieben |] ohne Leerzeichen) angegeben, so handelt es sich um eine optionale Alternativauswahl (kann man oder auch nicht). Wenn keine der Alternativen passt, dann wird auch keine dem Optionsausdruck ggf. zugeordnete Semantik erzeugt. Im obigen Beispiel wird also mit

     [<?MehrereZusammengefasst> Alternative3a | Alternative3b |]
    

    die Semantik MehrereZusammengefasst nicht erzeugt bei leerer Option.

    5.2.3 Auswahl-Option mit rechtsbündigem Test [|... | ... ]

    Topic=.ZBNF_syntaxDescription....

    Es ist vorgesehen, dass während der Syntaxprüfung zuerst die Fortsetzung nach der Optionsklammer geprüft wird. Nur wenn diese Syntax im aktuellem Zweig nicht passt, werden die Optionen getestet. Das ist beispielsweise in folgendem Konstrukt sinnvoll:

     [|-] <value>
    

    Value soll im Beispiel unter anderem eine Zahl, auch negativ, darstellen: value::=<#-?number>|<$?ident>. Wenn im Input eine Folge "-123" auftritt, dann soll nicht das negative Vorzeichen getrennt erkannt werden, sondern diese Folge als negative Zahl gelesen werden. Mit der Schreibweise [-]<value> würde man auf jeden Fall das negative Vorzeichen extra erkennnen. Syntaktisch ist das zwar egal, da aber eine Semantik dahinterliegt, kann der Unterschied schon bedeutungsvoll sein. Ein anderes Beispiel dafür ist:

     coplexlyNumberString::=[|<#?leftPart>\\.][|<#?middlePart>\\.]<#?rightPart>.
    

    Bei einer Angabe 123.456 würde man nach dieser Schreibweise semantisch den middle- und den RightPart bekommen. Bei der einfachen Optionsschreibweise würde man, da der Rightpart pflicht ist, einen Syntaxfehler bekommen, da die Zeichenkette als left- und middlePart verarbeitet wird. In diesem Fall könnte zwar streng formalsyntaktisch auch [<#?leftPart>\.][<#?middlePart>\.]<#?rightPart>\.. zulässig sein, es funktioniert so aber nicht. Selbstverständlich ist <#?leftPart>[.\<#?middlePart>][\.<#?rightPart>]. zulässig, hier wird die Eingabe optional mit fehlendem middle- oder Rightpart akzeptiert, aber es wird semantisch der leftPart erzeugt, nicht wie ggf. gewünscht der rightPart.

    Damit ist diese Schreibweise in der ZBNF die Möglichkeit, ein rechtsbündiges Parsen zu erreichen.

    5.2.4 Negativtest

    Topic=.ZBNF_syntaxDescription....

    Mit der Schreibweise

     [? syntax ]
    

    wird ausgedrückt, dass ein Input der angegebenen Form an dieser Stelle nicht passt. Typisch lässt sich dass bei Wiederholungen verwenden:

     Beispiel::={ [?;] <*;text> ; }.
    

    Ein text mit Abschluss-Semikolon kann beliebig oft vorkommen. Der Abschluss der gesamten Sequenz wird aber hier mit zwei Semikoli hintereinander ausgedrückt. Syntaktisch darf kein Semikolon direkt stehen, wenn zuvor ein text erwartet wird.

    5.3 Wiederholung { ... ? ... }

    Topic=.ZBNF_syntaxDescription...

    Wiederholungen waren in der alten BNF nicht vorgesehen, man verwendete die Rekursion für solche Aufgabenstellungen. In der EBNF wurde die syntaktische Angabe einer Wiederholung unter Verwendung der geschweiften Klammern {...} eingeführt. Im Gegensatz zum Verständnis der Wiederholung der meisten EBNF- Ausprägungen ist hier der mindestens einmalige Durchlauf Pflicht. Ist es gewünscht, dass die Wiederholung auch leer sein kann, so kann sie als Option angegeben werden:

     [{...}]
    

    Es gibt einen zweiten wichtigen Unterschied: Das Fragezeichen als Einleitung eines Rückwärtszweiges. Zwischen ? und } steht die Syntax, nach deren Auftreten eine weitere Wiederholung Pflicht ist, und die bei einer Wiederholung Pflicht ist. Das ist der häufig in der Praxis auftretende Fall, dass zwischen Aufzählungen ein bestimmtes Trennzeichen steht, aber nicht am Ende. Beispielsweise kann die in C bekannte enum-Definition wie folgt notiert werden:

     enumDefinition::=\\{ {<$name> [= <wert>] ? , } \\}.
    

    Hinweis: Zur enumDefinition gehört die { und } als Terminalsymbol, hier mit dem \ davor angegeben. Zwischen den Klammern muss wenigstens eine Definition stehen. Es können mehrere stehen, aber mit Komma getrennt.

    6 Syntax-Komponenten <syntax?semantik>

    Topic=.ZBNF_syntaxDescription..

    Syntaxkomponenten sind komplexe Syntaxausdrücke, die als extra Syntaxdefinition geschrieben werden und so die Übersichtlichkeit der Definition erhöhen. Das gab es auch schon in der originalen BNF, teilweise dort als Metamorphem bezeichnet: Vermittelndes Morphem. Gebräuchlich ist auch die Bezeichnung Nichtterminale oder Nichtterminalsymbol in Abgrenzung zu Terminalzeichen oder Terminalmorphem aus Sicht der Syntax. In der ZBNF wird die Bezeichnung Syntaxkomponente verwendet. Das trifft die eigentliche Bedeutung besser: Aus Sicht des damit geparsten Textes kann nicht von einem Morphem oder Symbol geredet werden, es ist ein komplexer Inhalt. Auch für den geparsten Inhalt (Parserergebnis) wird der Ausdruck Komponente in der Bedeutung: Teil eines Ganzen (was aber wieder aus mehreren Komponenten bestehen kann) gebraucht.

    Die Schreibweise in spiten Klammern <...> war auch schon in der BNF gebräuchlich. Andere Varianten sind die kursive Schreibweise. Diese ist aber nur bei gedruckten Texten möglich, nicht bei simple-ASCII. Die dritte Möglichkeit wäre die ohne besondere Kennzeichnung, in Form eines Bezeichner. Das setzt aber voraus, dass Terminalzeichen anders hervorgehoben sind. Leteres ist bei der EBNF in Gebrauch, Terminalzeichen werden dort in Anführungszeichen geschrieben.

    Für die ZBNF gilt adäquat wie originale BNF die einfache Form in spitzen Klammern geschrieben:

     <Syntaxbezeichner>
    

    Die Syntax ist als Syntaxdefinition irgendwo anders eigenständig definiert:

     Syntaxbezeichner::=Syntaxvorschrift.
    

    Die ZBNF zeichnet sich dadurch aus, dass nicht nur die Syntax definiert wird, sondern auch semantische Angaben formell mit definiert werden. Die Semantikbezeichnung ist ohne weitere Angaben identisch mit der Syntaxbezeichnung. Allerding ist bei der Syntaxdefinition auch eine abweichende Semantik angegebbar, wie im folgenden Kurzbeispiel einer Syntaxvorschrift demonstriert wird:

     {Rechnungsadresse: <Adresse?Rechnungsadresse> | Lieferadresse: <Adresse?Lieferadresse>}
    

    Beidemale wird syntaktisch eine <Adresse> verlangt, bestehend aus Name, Straße, Ort usw.Je nach Stellung im Gesamtkontext ist diese <Adresse> aber entweder die Lieferadresse oder Rechnungsadresse. Im Parser-Ergebnis wird an dieser Stelle der Semantikbezeichner für die Auswertung abgelegt. Die allgemeine Form dafür ist

     <Syntaxbezeichner?Semantikbezeichner>
    

    Es ist auch möglich, eine spezielle Semantik zu unterdrücken. Damit wird zwar die Syntax vorgeschrieben, aber an dieser Stelle keine Semantik erzeugt. Das wird geschrieben mittels:

     <Syntaxbezeichner?>
    

    Das Gleiche ist erreichbar, wenn <Syntaxbezeichner> bei der Verwendung geschrieben wird aber man in der Syntaxdefinition eine leere Semantik angibt:

     Syntaxbezeichner::=<?> Syntaxdefinition.
    

    Der Semantikbezeichner ist die gesamte Zeichenkette bis zu dem abschließendem ">". In der ZBNF gibt es keine Regeln für die Semantikbezeichner, aber die Auswertung muss damit zurecht kommen. Wird über ZBNF ein XML-File erzeugt, dann muss die Semantikbezeichnung den Regeln für Tagnamen in XML folgen. Zusätzlich ist es möglich, mit führendem "@" ein Attribut zu bestimmen. Außerdem gibt es die Möglichkeit, einen Pfad mit "/" getrennt anzugeben, und "." für die Bezeichnung des Textes zu einem Element, adäquat zu "text()".

    Es gibt einige Zeichen mit Sonderbedeutungen unmittelbar folgend nach dem einleitendem "?". Diese sind in den Abschnitten Innere Syntax von erkannten Zeichenketten und Transformation einer Semantik in eine andere Komponente angegeben.

    6.1 Spezielle Syntaxkomponenten: Bezeichner, Zahlen, ... <#...?>, <*...?...>

    Topic=.ZBNF_syntaxDescription...

    Folgende Angaben sind typisch für ZBNF:

     <#?Zahl>  <$?Bezeichner>  <*|*/?Zeichenkette>  <![=]*?RegularExpression>
    

    Bezeichner und Zahlen sind programmtechnisch wesentlich einfacher zu beherrschen als mit einem formellen Parsen, die Konvertierungsroutinen liegen fest. Die Syntax ist bekannt. Innerhalb von Nichtterminal-Symbolen in der Syntax kann eine bestimmte Menge dieser syntaktischen Einheiten angegeben werden, mit Rückgriff auf deren fest programmierte Syntax und Konvertierung. Die Grundstruktur dieser Angaben ist <symbol?semantic>. Zusätzlich kann die Anzahl der Zeichen begrenzt werden, in dem die Zeichenanzahl dezimal unmittelbar nach dem < angegeben wird. So steht

     <16*?cell>
    

    für die folgenden 16 Zeichen, egal welche. Auf diese Weise kann man beispielsweise Angaben in festen Spalten konvertieren.

    Folgende Angaben dürfen als Symbol angegeben werden: <symbol?...>

    symbol

    Bedeutung

    $

    Ein Bezeichner, so wie er in den bekannten Programmiersprachen Java, C gebräuchlich ist: Bestehend aus Buchstaben A bis Z und a bis z, den Ziffern und dem Unterstrich, aber nicht beginnend mit einer Ziffer.

    $addChars

    Der Bezeichner kann die angegebenen Zeichen zusätzlich enthalten, beispielsweise $- wenn auch das Zeichen - Bestandteil eines Bezeichners sein darf. Das ist beispielsweise bei XML-Bezeichnern der Fall. Will man damit beispielsweise einen Vornamen damit parsen, dann kann man von der Tatsache, dass auch Ziffern darinnen akzeptiert werden, gegebenenfalls absehen, da das ansonsten keine Widersprüche erzeugt. Um solche Konstruktionen wie "Hans-Dieter" richtig zu erkennen, ist die Angabe von $- ebenfalls die richtige Wahl, ebenfalls für die Zulassung von Umlauten. Beispiel: <$-ÄÖÜäöüß?Name>.

    #

    Steht für eine Ziffernfolge aus 0..9, die in eine positive Zahl konvertiert wird. Die Ziffernfolge darf nicht mit 0 beginnen oder darf nur aus der 0 bestehen.

    #-

    Ein negatives Vorzeichen als führendes Zeichen wird akzeptiert, es wird in eine positive oder negative Zahl konvertiert.

    #f

    Steht für eine Float-Zahl in der Java- oder C-üblichen Schreibweise. Intern wird eine Zahl im double-Format erzeugt.

    #f*Factor

    Steht für eine Float-Zahl, die während des Parsens mit dem angegebenem Faktor multipliziert wird. Factor muss eine Float-Zahl sein. Mit dieser Variante ist es beispielsweise möglich, gleich beim Parsen eine Multiplikation entsprechend einer Maßeinheit durchzuführen und den Ergebniswert als Parse-Ergebnis zu speichern. Beispiel: [<#f?length> mm| <#f*10?length> cm| <#f*25.4?length> inch] speichert einen Wert immer in Millimeter, die Weiterverarbeitung braucht sich nicht mehr um eine Maßeinheit zu kümmern. Ein anderes Beispiel ist die Möglichkeit, mit Dezimalpunkt getrennte Zahlen sofort als Integer weiterzuverarbeiten. Beispiel: Eine Angabe "56.34" wird mit <#f*100?Wert> in den Wert 5634 gewandelt. Die Möglichkeit, einen Werte ohne eigene Programmierung direkt speichern zu können ergibt sich mit der Kombination des ZBNF-Parsers mit dem Ablegen des Parser-Ergebnisses in eigenen Java-Instanzen.

    #x

    Eine hexadezimale Zeichenfolge wird erwartet. Die Hexaziffern a..f dürfen groß oder kleingeschrieben sein. Führende Nullen dürfen enthalten sein. Einleitende Zeichen wie das 0x bei den Programmiersprachen C, Java gehört nicht dazu. Um eine Zahl wahlweise als Dezimal- oder Hexazahl zu parsen, kann man schreiben: [0x<#x?number>|<#?number>]. Wird bei dieser Angabe eine Zeichenfolge 0x ohne Hexaziffern danach vorgefunden, dann wird die 0 als Ziffer 0 erkannt. Das x bleibt danach übrig, so dass sich ein Syntaxfehler in der weiteren Folge ergibt.

    *endchars

    Zeichen bis ausschließlich eines der hier angegebenen Zeichen. Die Zeichen unter endchars können beispielsweise auch ein \n für das Zeilenende sein. Wird keines der Zeichen aus endchars gefunden, dann gilt der Syntaxtest als nicht bestanden. Wird eines der terminierenden Zeichen direkt gefunden, dann ist das kein Syntaxfehler, es wird aber kein semantischer Eintrag abgelegt. Beispiel: <*\r\n?CharsUntilLineend>: Alle Zeichen bis Zeilenende, genau bis eines der Zeichen 0x0d (umschrieben mit \r) oder 0x0a (umschrieben mit \n).

    Beispiel: <*!\??CharsUntilExclamationOrQuestionMark>: Das das ? als Trennzeichen zur Semantik benutzt wird, wird es bei Angabe als eines der Endzeichen mit \? umschrieben. Es wird die Zeichenkette bis auschließlich einem ? eingelesen.

    Achtung: Dieses Konstrukt steht an sich im Widerspruch zum Gedanken des Parsens: Hier wird nicht auf Übereinstimmung geprüft sondern bis zur Nichtübereinstimmung gesucht. Es muss aufgepasst werden, dass die endchars passend angegeben werden. Ein typischer Fehler ist es, nur bis zu einem Leerzeichen zu suchen. Im Quelltext gibt es aber kein Leerzeichen nach der zu erkennenden Folge, sondern beispielsweise einen Zeilenumbruch, gefolgt sofort von einem anderen Quellteil am Anfang der neuen Zeile. Alles dies bis irgendwann ein Leerzeichen kommt wird dann dem <*\ ?semantic>-Konstrukt zugeordnet. Die Folge sind nachfolgenden Syntaxfehler, aber möglicherweise ist die Ursache nicht einfach zu erkennen.

    Hinweis: Die Folgen *| *"" *'' haben weitere Sonderbedeutungen, siehe unten. Daher können die Zeichen |"' nicht als erste Zeichen nach dem * in endchars aufgeführt werden. Entweder man dreht dann die Reihenfolge, oder man kann mit \| usw. umschreiben.

    *""endchars

    Zeichen bis ausschließlich eines der hier angegebenen Zeichen. Wird dabei ein Anführungszeichen erkannt, dann werden alle Zeichen bis zum Ende der Anführung nicht als endchar getestet. Die Umschreibung mit \" in der Anführung wird richtig erkannt, also nicht als Ende der Anführung. Wird ein Zeichen aus endchars außerhalb der Anführung nicht gefunden, dann gilt der Syntaxtest als nicht bestanden. Wird eines der terminierenden Zeichen direkt gefunden, dann ist das kein Syntaxfehler, es wird aber kein semantischer Eintrag abgelegt.

    Beispiel: <*""&?CharsUntilAmpersand>: Alle Zeichen bis zum Kaufmanns-Und, aber nicht ein & innerhalb einer Anführung.

    *|str|str

    Zeichen bis ausschließlich eines der hier angegebenen Zeichenketten. Die Zeichenketten, hier mit str bezeichnet, stehen jeweils nach einem Trennzeichen "|". Wird keine der terminierenden Zeichenketten aus endchars gefunden, dann gilt der Syntaxtest als nicht bestanden. Wird eines der terminierenden Zeichenketten direkt gefunden, dann ist das kein Syntaxfehler, es wird aber kein semantischer Eintrag abgelegt.

    Beispiel: <*|*/|@?description>: Eine Zeichenkette bis ausschließlich */ oder ausschließlich @ wird eingelesen und als description interpretiert.

    oLastChar:endchars

    Alle Zeichen bis ausschließlich eines der am weitesten hinten auftretenden Zeichen aus endchars.

    Das ist eine Sonderform, die nur bei relativ kurzen Eingabetexten sinnvoll anwendbar ist, aber auch insbesondere bei Verwendung der Inneren Syntax. Ein wichtiges Anwendungsgebiet ist das Separieren von Pfadangaben und Filenamen, wie im folgenden Beispiel gezeigt:

    something::= <* \\r\\n),?!testFilePath>  ##Innere Syntax für testFilePath,
                                          ##alle Zeichen bis whitespace oder ) ,
    testFilePath::=[<stringUntilLastChar:/\\\\:?path>[/|:|\\\\]]<*?name>.
    

    Im Beispiel werden zuerst alle Zeichen bis einem Whitespace oder Klammer zu oder Komma eingelesen und für eine innere Syntax testFilePath bereitgestellt. In der inneren Syntax wird dann bis zum am weitestens rechts der gesamten Eingabe vorkommenden Zeichen \ / oder : der Path erkannt. Wird keines solcher Zeichen gefunden, dann ist keine path-Angabe vorhanden. Nach erkannter Path-Angabe muss das vorgefundene Trennzeichen muss überlesen werden. Alle Zeichen bis zum Ende gelten dann als Name.

    *<<endchars

    Das ist die Kurzform für *untilLastChar:endchars, möglicherweise schwerer erfass- und merkbar, dafür kürzer.

    toLastCharIncl:endchars

    Alle Zeichen bis einschließlich eines der am weitesten hinten auftretenden Zeichen aus endchars.

    +<<endchars

    Das ist die Kurzform für +untilLastChar:endchars.

    *{indentchars}|str|str

    Zeichen bis ausschließlich eines der hier angegebenen Zeichenketten str. Es wird eine Einrückung berücksichtigt: Jeweils bei einer neuen Zeile werden die Zeichen indentchars überlesen, aber nur bis zu der Spalten-Position der ersten Zeile, bei dem die Zeichenkette beginnt. Endet die Angabe identchars mit einem Leerzeichen, dann werden Leerzeichen auch bis über die Spaltenposition hinweg ignoriert, andere Zeichen aber nicht. Diese Variante ist beispielsweise geeignet um Blockkommentare, wie sie in C- oder Java-Quelltexten üblich sind, zu verarbeiten. Ist beispielsweise folgender Text vorhanden:

      /** Das ist kommentar zu
        * * irgendeiner Methode
           Zeile beginnt weiter rechts
        Zeile beginnt weiter links
        */
    

    und dieser wird mit der Syntaxvorschrift /** <*{ * }|*/?comment>*/ geparsed, dann enthält das Ergebnis die Zeichenkette

     Das ist kommentar zu
     * irgendeiner Methode
     Zeile beginnt weiter rechts
     Zeile beginnt weiter links
    

    Zu beachten ist, dass eine Zeile mit einer geringeren Einrückung richtig erkannt wird. Eine weitere Einrückung wird genau dann nicht weggelassen (entgegen obenstehendem Beispiel), wenn als letztes Zeichen zwischen {...} kein Leerzeichen angegeben wird. Damit ist insbesondere die Arbeit mit einer Wikipedia-like-Textformatierung möglich. Dazu folgender Hinweis: Bei der Konvertierung nach XML wird die Wikipedia-like-Textformatierung dann umgesetzt, wenn als Semantik "p+" als letztes Element angegeben wird. Die Wikipedia-like-Textformatierung ist keine grundsätzliche Leistung der ZBNF, aber im ZBNF-XML-Umsetzer enthalten.

    ""

    Es wird eine Anführung erwartet. Geparsed werden alle Zeichen innerhalb einer Anführung einschließlich der umschließenden Anführungszeichen. Die Umschreibung mit \" in der Anführung wird richtig erkannt, ebenso wird die Umschreibung von \n \t usw. erkannt und als Sonderzeichen geparsed. Gültig ist, wenn im zu parsenden Text beispielsweise "Das ist ein \"Zitat\"." gefunden wird. Nicht gültig ist, wenn der Text an dieser Stelle nicht mit einem Anführungszeichen beginnt oder das schließende Anführungszeichen fehlt. Die Anführungszeichen sind diejenigen des Standard-ASCII (hexa 0x22), kein Zeichen aus erweiterten Codetabellen. Alle Zeilenumbrüche Leerzeichen usw. werden unverändert als Text in der Anführung verarbeitet. Soll eine Sonderzeichenerkennung in der Anführung nicht erfolgen, beispielsweise ein Text wie "Das ist ein Backslash: \" zurückgegeben werden, dann ist diese Kennzeichnung nicht zu verwenden, statt dessen reguläre Ausdrücke.

    ''

    Es wird eine Anführung in Hochkommata (ASCII=0x27) erwartet. Die Umschreibung mit \ ist ebenfalls aktiv.

    !

    Damit wird eine Angabe Regulärer Ausdrücke eingeleitet, siehe folgendes Kapitel.

    6.2 Reguläre Ausdrücke <!...?>

    Topic=.ZBNF_syntaxDescription...

    Reguläre Ausdrücke an sich sind bereits eine Möglichkeit, Texte zu parsen. Dabei kann angegeben werden, welche Teile der Regulären Ausdrücke Variablen zugewiesen werden, die dann für die Auswertung zur Verfügung stehen. Die Sprache Perl hat den Ruf, solche Verarbeitungen besonders gut zu unterstützen.

    Jedoch sind solche Ausdrücke, wenn sie komplexer sind, schwer lesbar und für eine Syntaxbeschreibung nicht gut tauglich.

    In der ZBNF können Regulären Ausdrücke als Erkennungs-Masken genutzt werden. Der Funktionsumfang der Regulären Ausdrücke entspricht dem der class java.util.regex.Pattern. der Implementierungssprache Java. Der reguläre Ausdruck wird in folgender Notation angegeben:

    .<!Regex?Semantik>

    Als Besonderheit ist zu beachten, dass ein Backslash \ vom Parser selbst als Einleitungszeichen von Sonderbedeutunngen benutzt wird. Man muss also den auch bei Regulären Ausdrücken üblichen Backslash doppelt angegeben. Folgend sind einige Beispiele aufgeführt (Auszug, vollständig siehe Beschreinbung von java.util.regex.Pattern):

    Regex

    Bedeutung

    .

    Ein einzelner Punkt steht für ein beliebiges Zeichen.

    .*

    Ein beliebiges Zeichen kann angegeben werden, das ganze aber beliebig oft, von 1 bis beliebig. Das * steht für die Häufigkeit der Folge. In diesem Fall bedeutet das, dass alles an Text kommen darf, bis zum Ende. Hier kommt allerdings die Begrenzung in ZBNF in der Form (Beispiel) <32?.*?Max32Chars> angegeben zum Ausdruck, außerhalb des Regulären Ausdruckes wird eine Maximalanzahl angegeben.

    .+

    Wie .*, es muss aber mindestens 1 Zeichen stehen, bei * auch kein Zeichen.

    [abc]

    Eines der zwischen den [] genannten Zeichen

    [abc]*

    Beliebig viele der zwischen den [] genannten Zeichen. Das ist wieder das bei .* genannte Muster. * gibt die Anzahl an. Davor steht die Auswahl an Zeichen auf jeder Position.

    [abc]+

    Adäquat .+, es muss mindestens 1 der genannten Zeichen vorkommen.

    [a-c]

    Eines der Zeichen in dem in [] genannten Bereich, im Beispiel zwischen a und c.

    [A-Za-z]

    Es können auch mehrere Bereiche angegeben werden. Hier sind alle Buchstaben gemeint.

    [A-Za-z]+

    Das ist ein Wort bestehend aus Groß- und Kleinbuchstaben frei gemischt.

    [A-Z][a-z]*

    Zuerst muss ein Grossbuchstabe kommen, danach beliebig viele Kleinbuchstaben.

    muss

    Das ist die Folge von ganz bestimmen Buchstaben (hier 'muss')

    Me.er

    Meyer oder Meier, aber auch Mexer, oder MeAer oder Me!er, der Punkt steht für jedes beliebige Zeichen.

    Me[yi]er

    Meyer oder Meier

    \\S

    Jedes Zeichen, was kein Whitespace ist. Hinweis: Das Backslash muss hier doppelt geschrieben werden, da beim Einlesen der Syntax ein einzelnes Backslash erst noch als Ankündigung einer Umschreibung wirkt.

    \\s

    Jedes Whitespace, dazu zählen Leerzeichen, Tabulator, Linefeed (Hexa 0a) und Carrige Return (Hexa 0d).

    \\w

    Ein Wort

    \\W

    Alles außerhalb eines Wortes

    7 Semantikangaben

    Topic=.ZBNF_syntaxDescription.semantic.

    Das "Z" in "ZBNF" steht als umgedrehtes "S" für Semantik. Ein Haupt-Anwendungsgebiet eines ZBNF-Parsers ist die Umwandlung von frei aber syntaktisch fassbaren Texten in XML. Die Semantik bestimmt dabei den Aufbau des als Ergebnis erzeugten XML-Baumes (-Files). Die Gliederung in Syntaxkomponenten bestimmt die Baumstruktur, die angegebene Semantik bestimmt die Tagnamen der Elemente.

    Beispielsweise erzeugt folgende Syntax:

     syntax::= { <kopf> { <data> } } -end-.
     kopf::= idx = <#?index>.
     data::= value = <#?wert>.
    

    mit folgenden Daten:

     idx=1 value=5 value=6 idx=123 value=7 value=23 -end-
    

    folgenden XML-Baum:

     <syntax>
       <kopf><index int="1" /></kopf>
       <data><wert int="5" /></data>
       <data><wert int="6" /></data>
       <kopf><index int="123" /></kopf>
       <data><wert int="7" /></data>
       <data><wert int="23" /></data>
     </syntax>
    

    An diesem Beispiel ist an sich auch schön sichtbar, was das Wesen von XML ist. Die Datenquelle vor dem parsen ist zwar wesentlich kürzer, benötigt aber die Information über die Syntax mit Semantik, um die Daten auch verwerten zu können. XML enthält in den Daten gleichzeitig die Semantik. ZBNF ist das Bindeglied zwischen beliebigen Daten und XML.

    7.1 Semantikangaben zu Syntaxkomponenten, Unterdrückung der Semantik

    Topic=.ZBNF_syntaxDescription.semantic..

    Eine Syntaxkomponente der Form <Name> hat eine bezeichnungsidentische Semantik. Die Semantik wird als Resultat des Parsers abgelegt. Modifikationen der Semantik sind möglich:

    • <Syntax?Semantik>: Die Semantik wird anders bezeichnet als die Syntax. Beispiele dafür sind oben angegeben.

    • <Syntax??> ist gleichbedeutend mit <Syntax?Syntax> oder auch nur <Syntax>.

    • <Syntax?>: Hierbei wird keine extra Semantik der Syntaxkomponente erzeugt. In der XML-Darstellung entfällt das einschließende Element. Die letzte Variante ist recht wichtig, wenn komplexe syntaktische Zusammenhänge beschrieben werden. Nicht in jedem Fall ist eine Syntaxkomponente von semantischer Bedeutung, oft ist es nur eine Gliederung der Syntax. Ein Beispiel dafür stammt aus der Syntaxvorschrift zur Konvertierung von Headerfiles (C-Programmierung:

     structDefinition::=struct [<$?typetagident>] \\{ { <structContent?> } \\} <$?name>;.
     classDefinition::=class [<$?typetagident>] \\{ { [ <classContent> | <structContent> ] } \\} <$?name>;.
     structContent::=<?>
        [ <unionDefinition>
        | <structDefinition>
        | <attribute>
        | <defineDefinition>
        | <structContentInsideCondition>
        ].
    

    In diesem Beispiel ist <structContent> zwar eine Syntaxkomponente, aber nur damit die Syntax übersichtlich geschrieben werden kann, auch weil dieser Baustein zweimal verwendet wird. <structContent> ist daher nicht semantisch von Bedeutung, die Inhalte unterhalb <structContent> sind es aber. Hier wird die Syntax von <structContent> auf zweierlei Art ausgeschaltet:

    • Bei dem Aufruf von <structContent?>

    • Bei der Definition von structContent::=<?>, siehe dazu auch bei Zusätzliche Semantikangaben am Anfang einer Syntaxvorschrift

    Die hier dargestellten Semantikregeln gelten gleicherweise auch für die speziellen Syntaxkomponenten wie <#?Semantik> oder <!Regex?Semantik>

    7.2 Zusätzliche Semantikangaben im Verlauf einer Syntaxvorschrift

    Topic=.ZBNF_syntaxDescription.semantic..

    Immer wenn eine Folge

    <?semantic>
    

    beim Parsen erfolgreich durchlaufen wird, dann wird ein Semantik-Element erzeugt. Das kann sinnvoll mit Terminalsymbolen verbunden werden, beispielsweise:

    Das ist das Haus vom [Nikolaus<?Nikolaus>|Weihnachtsmann]. In diesem Fall wird in der Variante 'Nikolaus' die Semantikinformation 'Nikolaus' erzeugt, beim 'Weihnachtsmann' wird aber nichts erzeugt.

    7.3 Zusätzliche Semantikangaben am Anfang einer Syntaxvorschrift

    Topic=.ZBNF_syntaxDescription.semantic..

    Ein spezialisierter Fall liegt vor, wenn die Folge

     [<?semantic> .... oder ::=<?semantic> ... oder {<?semantic>....
    

    direkt am Anfang einer (Teil-) Syntaxvorschrift ohne Whiteblank dazwischen steht. Hier gilt ebenfalls, das eine Semantik-Information erzeugt wird, in diesem Fall immer, wenn dieser gesamte Syntaxteil erfolgreich absolviert wurde. Zusätzlich wird in diese Semantik noch die Information eingeschrieben, welche Alternative und die wievielte Wiederholung dabei durchlaufen wurde. Ein typischer Fall der Anwendung ist

     [<?whichOption> a | b |]
    

    Hier wird bei Zutreffen von 'a' der Wert 1 als Auswahl vermerkt, bei 'b' der Wert 2 und bei keinem Zutreffen der Wert 0. Damit ist für die Auswertung erkennbar, welche Auswahl getroffen wurde.

    Das obige Beispiel der XML-Konvertierung ist folgend mit etwas geändertem Inhalt und Syntax/Semantik angegegen. In der Syntax wurde ein <?dataBlock> eingefügt. Die Komponenten <data> und <info> erzeugen aber dafür kein eigenes Element:

     syntax::= { <kopf> {<?dataBlock> <data?> | <info?> } } -end- .
     kopf::= idx = <#?index>.
     info::= info = <""?info>.
     data::= value = <#?wert>.
    

    damit wird folgender XML-Baum erzeugt:

     <syntax>
       <kopf><index int="1" /></kopf>
     	<dataBlock repetition="1" alternative="1"><wert int="5" /></dataBlock>
       <dataBlock repetition="2" alternative="1"><wert int="6" /></dataBlock>
       <dataBlock repetition="3" alternative="2"><info string="null" src="&quot;Index1&quot;" /></dataBlock>
       <kopf><index int="123" /></kopf>
       <dataBlock repetition="1" alternative="1"><wert int="7" /></dataBlock>
       <dataBlock repetition="2" alternative="1"><wert int="23" /></dataBlock>
     </syntax>
    

    7.4 Erklärung der Semantik im ZBNF-script als Hilfestellung

    Topic=.ZBNF_syntaxDescription.semantic.semanticHelp.

    Neu seit 2008-July:

    Im Syntaxscript kann mit folgender Schreibweise am Beispiel

    ?en:mySemantic::= "Explanation text. It may be more detailed.
      It is a helpness."
    ?de:mySemantic::= "Erklärungstext, etwas umfangreicher als Hilfestellung".
    

    eine etwas längere Erklärung der vorhandenen Semantiken erfolgen. Damit wird eine Beschreibung einer Syntax/Semantik ausschließlich im ZBNF-Script, ohne weitergehende Dokumentation tatsächlich möglich. Für eine Weiterverarbeitung interessiert im wesentlichen die Semantik. Für die formale Schreibweise interessiert die Syntax. Beides miteinander definieren das Wie und Was. Zu beachten ist der abschließende Punkt nach dem Ausführungszeichen.

    Für das Parsen wird diese Information nicht benötigt. Es ist vorgesehen, die Semenatikerläuterung zu benutzen, wenn ein Spezialeditor, etwa in Form eines Eclipse-Plugin, bereitgestellt werden wird. Dieser kann einen Text mit dem zugehörigen ZBNF-Script während der Editierung parsen, baumartig darstellen (outline) und zu jedem semantischen Element die hier notierte Hilfe anbieten.

    Zu beachten ist, dass eine Semantik kontextspezifisch sein kann, eine gleichbezeichnete Semantik an verschiedenen Stellen unterschiedliche Bedeutung hat. Dazu schreibe man den semantischen Kontext davor, beispiel:

     ?de:myComponent/mySemantic::="...".
    

    Die Angabe in verschiedenen Sprachen soll in einem Plugin-Editor eine sprachspezifische Auswahl unterstützen. Das Sprachkürzel sollte den üblichen www-Kennzeichnungen folgen.

    8 Innere Syntax von erkannten Zeichenketten <syntax?!innerSyntax>

    Topic=.ZBNF_syntaxDescription..

    Eine geparste Zeichenkette insbesondere mit <*endchars?...> oder <*|endstr?...> oder <""?...> kann gegebenfalls ein innere Syntax enthalten. Der äußere Syntaxtest erkennt zunächst die gesamte Zeichenkette nach dem äußeren Kriterium, also bis zu bestimmten Ende-Zeichen oder den Text in Anführungszeichen. Danach wird eine Syntaxerkennung und Semantik-Umsetzung mit dem eigentlichen Inhalt ausgeführt. Dessen Ergebnis wird im ParserStore abgelegt.

    Dazu muss folgendes notiert werden:

     <...?!syntax>
    

    Hier wird keine Semantik angegeben, sondern nach dem Ausrufezeichen der Bezeichner der Syntaxvorschrift zum inneren parsen der erkannten Zeichenkette. Das Parsen wird allerdings erst dann erledigt, wenn das äußere Parsen abgeschlossen ist.

    9 Transformation einer Semantik in eine andere Komponente

    Topic=.ZBNF_syntaxDescription..

    Es gibt Fälle, bei denen Informationen syntaktisch außen stehen, die aber semantisch möglicherweise dupliziert zu mehreren Textelementen gehören. Ein Beispiel dafür ist die aus C oder Java bekannte abgekürzte Schreibweise bei der Definition von Variablen:

     /**Beschreibung gültig für alle Variablen. */
     int a,b,c;
    

    Die Variablen a, b, und c sind alle drei vom Typ int, die Beschreibung gehört auch an alle drei Variable. Es soll inhaltlich kein Unterschied sein zu der in C und Java gleichbedeutend ausführlichen Form:

     /**Beschreibung gültig für alle Variablen. */
     int a;
     /**Beschreibung gültig für alle Variablen. */
     int b;
     /**Beschreibung gültig für alle Variablen. */
     int c;
    

    Die ZBNF kennt die Möglichkeit, innerhalb einer Syntaxangabe Inhalte einer Syntaxkomponente einer andere Komponente zuzuordnen. Im vorliegenden Beispiel wird das wie folgt ausgeführt:

     attribute::=[/**<description?-?>*/] <type?-?> {<attributedef?+attribute>  ?,};.
    

    Das Beispiel ist ein Ausschnitt aus Cheader.zbnf. Wird ein ?- statt dem einfachen ? vor der Semantik geschrieben, dann wird der beim Parsen erhaltene Inhalt in einem extra Parser-Store zugehörig zur Syntaxvorschrift gespeichert. Mehrere solcher Angaben werden kummuliert. Also werden hier die Beschreibung und die Typangabe aufgesammelt. Das ? hinter dem - bedeutet, dass die Semantik identisch mit der Bezeichnung der Syntaxkomponente ist. Es handelt sich also um eine Kurzschreibweise anstatt <description?-description> beziehungsweise <type?-type>. Die <attributedef?+...> nimmt nun den zuvor geparsten Inhalt auf, und zwar bei jedem geparsten Attribute extra. Die Attribute stehen in einer Wiederholungsklammer {...}. Schlüssel dafür ist das ?+.

    Wichtig ist dieses Konstrukt, wenn Informationen in einem XML-Baum innen erscheinen sollen (zugehörig zu einzelnen Elementen), jedoch in einer vorliegenden Textform nur einmal am Anfang aufgeführt werden sollen. Die Textform wird damit gegenüber dem XML vereinfacht. Allgemeines Beispiel:

     syntax::=<KopfInfo?-?> { <Element1?+?> | <Element2?+?> | <Element3?+?> }.
    

    Im XML soll die KopfInfo jeweils zum Elememt zugehörig erscheinen:

     <Element1><KopfInfo>...</KopfInfo> ... </Element1>
     <Element1><KopfInfo>...</KopfInfo> ...andere Daten ... </Element1>
     <Element2><KopfInfo>...</KopfInfo> ... </Element2>
    

    Hinweis: Diese Möglichkeit des Parsens wurde zunächst nicht für die oben vorgestellten Fälle angedacht, sondern für eine Parser-Ablauf-Vereinfachung. Tritt ein syntaktisches Element mehrfach am Anfang von Varianten auf:

     syntax::=[ <Variante1> | <Variante2> | <Variante3> ]
     ....
     Variante1::= <KopfInfo> <RestVariante1>.
     Variante2::= <KopfInfo> <RestVariante2>.
     Variante3::= <KopfInfo> <RestVariante3>.
    

    dann ist es unrationell, bei jeder Variante die eigentlich dort zugehörige Kopfinfo mehrfach zu prüfen. Daher erschien es günstiger, diese syntaktisch herauszuziehen aber inhaltlich dennoch innen anzuordnen:

     syntax::=<KopfInfo?-?> [ <Variante1?+?> | <Variante2?+?> | <Variante3?+?> ]
    

    Das Beispiel funktioniert auch, allerdings ist die umfangreiche Anwendung nicht unbedingt strukturfördernd für die syntaktische Schreibweise. Besser ist es, die Arbeitsweise im Parser innen zu optimieren, in dem ein geparster Anteil wiederverwendet wird, wenn gleiche Syntaxbedingungen vorliegen. Das ist also dann nicht nach außen sichtbar und muss nicht von der Anwendung beachtet werden.

    10 Zeichencodierungen

    Topic=.ZBNF_syntaxDescription..

    .

    10.1 Festlegung der Zeichencodierung der ZBNF-Script-Datei

    Topic=.ZBNF_syntaxDescription...

    Es gibt mindestens zwei gleichberechtigte Zeichencodierungen (englisch: encoding) in Dateien: Das bei Windows übliche 8-bit-Zeichenformat genormt nach ISO-8859-1, und UTF-8. Erläuterungen zu Zeichenformaten siehe Wikipedia-ASCII. Das UTF-16-Format spielt in Codierungen in Dateien kaum eine Rolle, es hat sich dort nie durchgesetzt und ist vom UTF-8 verdrängt worden. Dennoch sollte es benutzt werden können. Grundsätzlich soll der ZBNF-Parser auch Codierungen in fremden Sprachen beherrschen. - Damit besteht die Notwendigkeit, mehrere Zeichencodierungen zu beherrschen.

    Das Problem der Zeichencodierungsangabe wurde im XML-Standard gelöst: In der Kopfzeile wird die Codierung angegeben. Da die Kopfzeile ausschließlich mit dem Zeichenvorrat geschrieben wird, die dem ASCII (7 bit) entspricht, dieser Teil der Codierung ist in allen 8-bit-Zeichencodierungen identisch, kann diese Kopfzeile ohne Kenntnis der Codierung auf jeden Fall ausgewertet werden. Um auch UTF-16 zu beherrschen, wird der Inhalt der Kopfzeile mit diesem Format eingelesen, wenn die Kopfinformation beim Einlesen im 8-Bit-Format fehlerhaft ist.

    Diese Lösung ist für ZBNF übertragen worden. Die Kopfzeile einer ZBNF-Script-Datei kann wie im Beispiel gezeigt mit "<?ZBNF-www.vishia.de" beginnen, soll nach dem Text "encoding=" in Anführungszeichen den Namen des Zeichensatzes enthalten. Der Eintrag muss mit "?>" abschließen.

    Für den Fall, dass ein ZBNF-Script nicht aus einer Datei gelesen wird sondern von einem Java-Programm vorgegeben wird, braucht es diese Encoding-Angabe und die Startzeile nicht. Innerhalb Java sind in Strings alle Inhalte in UTF-16 codiert.

    10.2 Festlegung der Zeichencodierung der zu parsenden Datei

    Topic=.ZBNF_syntaxDescription...

    Grundsätzlich gilt, dass zu dem Inhalt einer zu parsenden Datei keine Angaben gemacht werden können außer durch das ZBNF-Script selbst. Damit kann es sinnvoll sein, dass die Zeichencodierung einer zu parsenden Datei im ZBNF-Script festgelegt ist. Das erfolgt mittels Angabe des Defines im ZBNF-Script (Beispiel):

     $InputEncoding=iso-8859-1.
    

    Es kann aber auch der Fall eintreten, dass die Codierung nicht Im ZBNF-Script festgelegt werden kann oder soll. Dann gibt es zwei weitere Möglichkeiten:

    • Die Codierung wird als Aufrufargument beim Kommandozeilen-Aufruf des Parsers angegeben.

    • Die Codierung steht in der Datei selbst.

    • Die letzte Variante ermöglicht eine Speicherung der Information in verschiedenen Zeichencodierungen, ohne dass das ZBNF-Script daraufhin angepasst werden muss, eine wichtige Möglichkeit. Es ist dabei voraussetzbar, dass die Codierung in der ersten Zeile der Datei enthalten ist, diese erste Zeile muss allgemein eine Schlüsselzeile sein, die ausschließlich im ASCII (7 bit) codiert ist. Mit welchem Schlüsselstring der Codierungsstring eingeleitet wird, kann dagegen nicht vorausgesetzt werden. Daher gibt es in der ZBNF die Möglichkeit, dieses Schlüsselstring festzulegen:

     $InputEncodingKeyword="charset=".
    

    Das Schlüsselwort wird in Anführungszeichen eingegeben. Nach diesem Schlüsselstring wird die erste Zeile durchsucht. Eine Codierung kann danach mit oder ohne Anführungszeichen stehen, beides wird akzeptiert. Eine Codierungsangabe in Anführungszeichen wird bis zum Ausführungszeichen akzeptiert, eine Codierungsangabe ohne Anführungszeichen wird als Identifier (Buchtstaben, Ziffern, Unterstrich) mit dem Zusatzzeichen "-" akzeptiert.

    Ist die Codierungsangabe keinem verarbeitbarem Zeichensatz zuweisbar, dann wird das Parsen mit einer Fehlermeldung abgebrochen. Das gilt sowohl für das ZBNF-Script als auch für die Input-Datei.

    10.3 Behandlung von Leerzeichen und Kommentar zwischen den ZBNF-Ausdrücken und Definitionen

    Topic=.ZBNF_syntaxDescription...

    Leerzeichen und Zeilenumbrüche zwischen ZBNF-Ausdrücken und Definitionen werden als whitespaces überlesen. Kommentare mit ## eingeleitet gelten bis zum Zeilenende und werden ebenfalls überlesen. Das ist das gleiche Verfahren wie innerhalb von ZBNF-Ausdrücken.

    Ein whitespace zwischen ZBNF-Ausdrücken hat keine Bedeutung. Demgegenüber kann es innnerhalb eines ZBNF-Ausdruckes zur Aufforderung zum Überlesen von Whitespaces im Input-String dienen, wie im Abschnitt "WhitespaceParsing" beschrieben.