Aus Linux-Magazin 06/2008

Editoren mit eigenen Syntaxdefinitionen erweitern

© sxc.hu

Programmcode schreiben ohne Syntax-Highlighting ist mühsam und fehlerträchtig. Damit auch Liebhaber seltener Sprachen in den Genuss der praktischen Lesehilfe kommen, untersucht diese Bitparade, wie und wie gut sich in Kate, Emacs und Netbeans neue Sprachen integrieren lassen.

Gängige Editoren färben die Syntaxelemente der Programmiersprachen unterschiedlich ein. Das erleichtert die Lesbarkeit von Programmcode. Tippfehler fallen außerdem sofort auf und lassen sich schnell korrigieren, wenn sich der Cursor noch in der Nähe befindet.

Unterstützung für gängige Sprachen bringen die Editoren im Lieferumfang mit. Viele Anwendungen erlauben es dem Benutzer zusätzlich, selbst Syntax-Highlighting-Regeln für neue Sprachen zu schreiben. Diese Bitparade vergleicht, wie gut sich bei den Editoren Emacs, Kate und Netbeans im Lieferumfang nicht unterstützte Sprachen in das Syntax-Highlighting einbinden lassen.

Kate

Syntaxauszeichnung setzt eine Grammatik voraus: Bestimmte Wortarten sind je nach Kontext unterschiedlich auszuzeichnen. Das Syntax-Highlighting des Standard-KDE-Editors Kate (Abbildung 1) setzt daher auf ein Kontext-Konstrukt. Innerhalb der Kontexte legen Regeln – oft reguläre Ausdrücke – bestimmte Aktionen fest. Das XML-Konstrukt

<context name="Normal Text" >
  <DetectChar attribute="String" context="string"char="&quot;" />
</context>

schaltet beim Auftreten eines Anführungszeichens in den Kontext »String« um. Dort gelten eigene Regeln.

Die Anweisung »DetectChar« sucht nach einzelnen Zeichen. Für die Suche nach ganzen Wörtern gibt es »StringDetect« und »RegExpr«, die Regular Expressions benutzt. Außerdem gibt es die Befehle »Detect2Chars« und »DetectAnyChar«, die nach Zeichen aus einer Liste suchen, und »keyword«, der nach bestimmten Wörtern in einer Liste sucht, sowie Suchbefehle für Ganz- und Gleitkommazahlen und einige weitere.

Dafür, dass der Kontext »String« beim erneuten Auftreten eines Anführungszeichens wieder endet, sorgt beispielsweise folgende Regel:

<context name="string" attribute="String">
  <DetectChar attribute="String" context="#pop" char="&quot;" />
</context>

Kontexte lassen sich beliebig verschachteln. »#pop« veranlasst das Syntax-Highlighting dazu, eine Ebene zurückzuspringen, »#pop#pop#pop« springt drei Kontextebenen tiefer.

Abbildung 1: Kate besitzt ein leistungsfähiges Syntax-Highlighting, das Grammatiken logisch nachvollziehbar über ein »Kontext«-Konstrukt umsetzt. Seit einiger Zeit ist die Regelsprache auch ausreichend dokumentiert.

Abbildung 1: Kate besitzt ein leistungsfähiges Syntax-Highlighting, das Grammatiken logisch nachvollziehbar über ein »Kontext«-Konstrukt umsetzt. Seit einiger Zeit ist die Regelsprache auch ausreichend dokumentiert.

Stilfragen

Für den Stil »String« aus der »attribute«-Anweisung im Kontext »String« legt der »itemDatats«-Block in der XML-Datei die Schriftformatierung fest:

<itemDatas>
  <itemData name="Keyword" defStyleNum="dsKeyword" />
  <itemData name="String" defStyleNum="dsNormal" color="#3C60E1" />
</itemDatas>

»defStyleNum=”dsKeyword”« übernimmt den in Kate für Schlüsselwörter vorgesehenen Standardstil unverändert, »defStyleNum=”dsNormal” color =”#3C60E1″« übernimmt die Schriftart des Standardtextes, definiert aber die Textfarbe neu.

Einfach oder komplex

Die Verschachtelung von Kontext, Regel und »itemData« sorgt beim Syntax-Highlighting für eine kontextsensitive Grammatik. Ist eine solche nicht gefragt, geht es auch einfacher: Regeln können nämlich, ohne den Kontext umzuschalten, direkt Stile (»String«) oder Schriftformatierungen (»color=”#3C60E1″«) zuweisen.

Zudem lassen sich auch direkt im Kontext bestimmte Standardsprünge festlegen: »lineEndContext=”Kontextname”« springt am Zeilenende zum angegebenen Kontext, »#pop«-Anweisungen sind auch hier möglich. »fallthrough=”true” fallthroughContext=”Kontextname”« bewirkt, dass der Parser zu einem bestimmten Kontext springt, wenn keine seiner Regeln zutrifft.

Sehr dynamisch

Besonders hervorzuheben ist das Kontextattribut »Dynamic«. Steht es auf »true«, so erinnert sich das Syntax-Highlighting im Kontext an alle mit Klammern gekennzeichneten Unterausdrücke aus der aufrufenden Regel. Das folgende Listing definiert eine (noch nicht ganz perfekte) Hervorhebung der in vielen Sprachen anzutreffenden Heredoc-Syntax »<<<Token String Token« mit dem Textstil »rot«:

<context name="Normal Text"  >
    <RegExpr context="heredoc" String="<<<(.*)"/>
</context>
    <context name="heredoc" dynamic="true" attribute="rot" lineEndContext="#stay">
    <RegExpr String="%1" context="#pop" dynamic="true" />
    </context>

Trifft das Highlighting auf den String »<<<<(.*)«, wechselt es in den Kontext »heredoc«. Der Unterausdruck »(.*)« fängt den Begrenzer-String ein, den eine Regel im Heredoc-Kontext über die Referenz »%1« nutzen kann, um den Heredoc-Kontext wieder zu beenden.

Dies löst das Problem effizienter als ein mehrzeiliger Ausdruck über den ganzen Heredoc-Block, denn die Regular-Expressions-Engine muss nicht den gesamten String einlesen. Vor allem aber lassen sich im Kontext »heredoc« weitere Regeln definieren, zum Beispiel solche, die Variablen einfärben.

Ausgereift ist die vorgestellte Heredoc-Regel allerdings noch nicht, weil sie den einleitenden Text »<<<Token« gleich einfärbt wie den String, das abschließende Token jedoch gar nicht. Abhilfe schafft das Regelattribut »lookAhead=”true”«, das dafür sorgt, dass eine Regel den erkannten Text nicht verschlingt. So lassen sich für die Heredoc-Einleitung und den Schluss eigene Regeln schreiben. Wie die XML-Dateien für das Kate-Synax-Highlighting insgesamt gültig aufzubauen sind, beschreiben das mit dem Editor mitgelieferte Handbuch oder [2].

Emacs

Emacs ist alt. Inzwischen selbstverständliche Konzepte tragen daher oft andere Namen als die heute üblichen: Fenster heißen Frames, das Einfügen von Text, das alle Welt Paste nennt, heißt Yank und das Färben von Syntaxelementen, das normalerweise Syntax-Highlighting heißt, nannten die Emacs-Entwickler Font-Lock. Offene Dateien heißen in Emacs Buffer. Von konfigurierbaren Modes, wie Emacs auf bestimmte Programmiersprachen abgestimmte Funktionsweisen nennt, reden die Entwickler neuerer Editoren nicht mehr, obwohl der Begriff eigentlich sehr treffend ist.

Die Konfigurierbarkeit der Emacs-Modes (Abbildung 2) geht so weit, dass dabei sogar eigene Parser und andere Mechanismen zum Einsatz kommen können. Beispiele hierfür sind der NXML-Mode [3] oder der erst vor Kurzem vorgestellte Js2-Mode [4]. Das sind jedoch Ausnahmen. In diesem Artikel geht es nur um Font-Locks, die den Standardparser von Emacs nutzen.

Abbildung 2: Das Syntax-Highlighting von Emacs nutzt einen Lisp-Dialekt. Für mit C-ähnlichen Sprachen Aufgewachsene ist Emacs-Lisp gewöhnungsbedürftig, doch die Leistung der nicht mehr jungen Software stimmt nach wie vor.

Abbildung 2: Das Syntax-Highlighting von Emacs nutzt einen Lisp-Dialekt. Für mit C-ähnlichen Sprachen Aufgewachsene ist Emacs-Lisp gewöhnungsbedürftig, doch die Leistung der nicht mehr jungen Software stimmt nach wie vor.

Lokalkolorit

Einem Dateiformat ist in Emacs ein so genannter Major-Mode zugeordnet. Er stellt dem Benutzer Hilfsmittel zum Arbeiten in einer bestimmten Sprache zur Verfügung. Dazu gehören mindestens das Font-Locking und eine auf die Sprache abgestimmte Funktion zum Einrücken von Zeilen (Indent).

In Emacs lassen sich Buffern (geöffneten Dateien) lokale Variablen zuordnen. Auch der Font-Locking-Mechanismus nutzt dieses Verfahren. Ein Major-Mode definiert die Variable »font-lock-defaults« in Bezug auf Buffer lokal und legt dort die Konstrukte ab, mit deren Hilfe Emacs Text einfärbt. [5] beschreibt das prinzipielle Vorgehen beim Erstellen eines Major-Mode. Der Quelltext könnte folgendermaßen aussehen:

(make-local-variable 'font-lock-defaults)
(setq font-lock-defaults '(mail-font-lock-keywords t t))

Der Aufruf der Funktion »make-local-variable« deklariert zunächst die Variable »font-lock-defaults« als lokal für einen Buffer. Dabei steht vor dem Namen der Variablen ein einfaches Anführungszeichen, um eine Evaluation der Variablen zu verhindern (vergleiche Kasten “Lisp-Crashkurs”). Die dann folgende Zeile weist der Variablen »font-lock-defaults« einen Wert zu. Der Aufruf von »setq« erlaubt es dabei, das einfache Anführungszeichen wegzulassen: Das »q« in »setq« steht für Quote.

Lisp-Crashkurs

Der in Emacs eingesetzte Lisp-Code besteht hauptsächlich aus in runden Klammern eingefassten Listen, Leerzeichen trennen die einzelnen Elemente voneinander. Das erste Element einer solchen Liste betrachtet Emacs als Funktion und ruft sie mit den anderen Elementen auf. Einfache Anführungszeichen unterbinden diese Evaluation. Die Listen lassen sich schachteln [1].

Der neue Wert der Variablen ist wieder eine Liste, deren erstes Element die Variable »mail-font-lock-keywords« darstellt. Die weiteren Argumente (im Beispiel zweimal »t«) betreffen Details des Emacs-Font-Locking, die eine in Emacs eingebaute Dokumentation des Font-Locking-Mechanismus [6] erklärt.

Geschachtelte Listen

Die eigentliche Konfiguration des Font-Locking-Mechanismus erfolgt über das erste Element der Liste. Es enthält selbst wieder eine Liste. Als Beispiel zeigt das folgende Listing eine stark vereinfachte Form des mit Emacs installierten Sendmail-Major-Mode, der im Emacs-Installationsverzeichnis zu finden ist – bei einem GNU Emacs 22 unter Gentoo ist dies »/usr/share/emacs/22.1/lisp/mail/sendmail.el«.

(defvar mail-font-lock-keywords
  (list
   '("^\(To\|Newsgroups\):". font-lock-function-name-face)
   '("^\(B?CC\|Reply-to\|reply-to\):". font-lock-keyword-face)
   '("^\(Subject:\)[ t]*\(.+\)?" (1 font-lock-comment-face))))

Der Inhalt von »mail-font-lock-keywords« ist wieder eine Liste. Jedes Listenelement besteht aus zwei Teilen, einem regulären Ausdruck, der Textschnipsel erkennt, sowie dem Namen der Schriftart, mit der Emacs die erkannten Substrings einfärbt. Schriftarten heißen in Emacs Faces. Das Font-Lock-Paket definiert einige Schriften vor, damit ähnliche Konstrukte in verschiedenen Major-Modes auch gleich eingefärbt aufleuchten.

In Emacs-Lisp sind reguläre Ausdrücke normale Zeichenketten, sie unterliegen also deren Regeln. Ein Backslash erzeugt daher eine Escape-Sequenz. Die Backslashes in den regulären Ausdrücke sind daher immer mit einem weiteren Backslash zu entschärfen.

Der obige Beispielcode matcht die Wörter »To« oder »Newsgroup« gefolgt von einem Doppelpunkt am Anfang einer Zeile. Den erkannten Strings weist Font-Lock dann die über die Variable »font-lock-function-name-face« festgelegte Schriftart zu. Die Variable »font-lock-keyword-face« enthält dagegen die Schriftart für die Felder »CC«, »BCC« und »Reply-to«.

Die dritte Auszeichnung, die den Betreff »Subject« einfärbt, geht einen Schritt weiter. Sie weist dem Text nicht einfach eine Schriftart zu. Vielmehr folgt auf den regulären Ausdruck eine Liste aus zwei Elementen. Bei dem ersten handelt es sich um die Zahl 1. Sie verweist auf die erste durch runde Klammern festgelegte Gruppe in dem regulären Ausdruck. Erst das zweite Listenelement nennt dann die Variable, in der die zu verwendende Schriftart steht.

Außer gleich direkt über die Variable »font-lock-defaults« kann die syntaktische Analyse auch mit von Font-Lock fertig zur Verfügung gestellten Konstrukten wie Kommentaren oder Zeichenketten-Konstanten arbeiten. Dabei kommt die Variable »font-lock-syntactic-keywords« zum Einsatz. Details darüber sind in der eingebauten Emacs-Dokumentation zu finden, die der Anwender über die Tastenkombination [Strg]+[H], [V], »Variablen-Name«, [Eingabe] aufruft.

Freiheit und Grenzen

Die Variable »font-lock-defaults« kann außer den vorgestellten noch weitere Konstrukte mit anderen Funktionen aufnehmen. Bei der Zuweisung von Werten zu den genannten Variablen kann der Entwickler außerdem auf den gesamten Funktionsumfang von Emacs-Lisp zurückgreifen. Das Font-Locking bietet also viele Freiheiten.

An seine Grenzen stößt der Mechanismus jedoch, wenn es darum geht, Syntaxelemente, die sich über mehrere Zeilen erstrecken, zuverlässig zu erkennen. Prinzipiell bezieht Font-Locking zwar den ganzen Puffer ein. In der Praxis betrachtet die Software aber meist nur einen kleinen Ausschnitt des Textes an der Stelle, die der Benutzer gerade bearbeitet.

Die bisher beschriebenen Mechanismen funktionieren sowohl im Emacs als auch im Xemacs. Für das seit einiger Zeit immer häufiger genutzte bequeme Font-Locking mit »font-lock-add-keywords« gibt es dagegen bei Xemacs noch Einschränkungen, [7] erläutert die Details.

Anpassungen

Font-Lock selbst lässt sich über Variablen in den Init-Dateien von Emacs oder über die »Customize« anpassen. Die Eingabe von [Alt]+[X], »customize-group«, [Eingabe], »font-lock«, [Eingabe] öffnet einen speziellen Buffer im Customize-Mode auf dem Schirm. Besonders wichtig sind hier die Variablen »global-font-lock-mode«, die Font-Lock aktiviert, »font-lock-maximum-size«, die die maximale Buffer-Größe festlegt, für die Font-Lock zum Einsatz kommen soll (Highlighting kann bei großen Buffern spürbare Verzögerungen bewirken), und »font-lock-maximum-decoration«, die festlegt, ob Font-Locking alle oder nur die wichtigsten Syntax-Konstrukte hervorhebt.

Die Dokumentation von Emacs-Lisp empfiehlt Entwicklern von Major-Modes für das Einfärben drei Stufen. Die erste sollte schnell arbeiten und nur Funktionsdefinitionen und -deklarationen, Kommentare, Zeichenketten und Include-Direktiven einfärben. Die zweite Stufe kümmert sich um Schlüsselwörter und (eingebaute) Datentypen, während die dritte Stufe, deren Ausführung spürbare Verzögerungen mit sich bringen darf, tiefer gehende Auszeichnungen vornimmt. Was die Performance angeht, ist der Font-Locking-Mechanismus von Emacs also unübertroffen flexibel.

Netbeans mit Schliemann

Der Archäologe Heinrich Schliemann ist der Namenspatron für die Unterstützung neuer Programmiersprachen in der vorwiegend auf Java spezialisierten IDE Netbeans. Mit diesem Modul erweitern auch Anwender ohne Programmierkenntnisse Netbeans innerhalb von Stunden um weitere Sprachen (Abbildung 3). Ergebnisse sind ein Syntax-Highlighting und eine Anzeige der Dateistruktur im Navigator sowie Code-Folding, mit dem der Benutzer Codeblöcke wie die Körper von Funktionen oder den Inhalt von Schleifen ausblenden kann.

Abbildung 3: Dank Schliemann bietet Netbeans Syntax-Highlighting und Code-Folding für neue Sprachen.

Abbildung 3: Dank Schliemann bietet Netbeans Syntax-Highlighting und Code-Folding für neue Sprachen.

Kernstück einer Syntax-Highlighting-Definition ist die Sprachbeschreibung in der so genannten NBS-Datei. Sie enthält neben der eigentlichen Sprachdefinition noch Anweisungen, die die Farbe der definierten Elemente festlegen. Auch die Definition des Code-Folding beziehungsweise der Dokumentstruktur, die Netbeans im Navigator anzeigt, erfolgt in dieser Datei. Sie kann zudem Erweiterungen der Syntax-Highlighting-Engine in Java enthalten.

Listing 1 demonstriert mit einem Syntax-Highlighting für das STEP-Datenaustauschformat [8] aus dem CAD-Bereich einen Teil der Funktionen und Möglichkeiten von Schliemann. Den vollen Funktionsumfang erläutert das ausführliche Tutorial unter [10].

Listing 1: NBS-Datei

01 TOKEN:ISO:("ISO-10303-21")
02 TOKEN:END_ISO:("END-ISO-10303-21")
03 TOKEN:whitespace:( [" " "t" "n" "r" ]+ )
04 TOKEN:line_comment: ("//"[^"n""r"]*)
05 TOKEN:keyword: ( ["!"]? ["A"-"Z"] ["A"-"Z" "0"-"9" "_" ]*)
06 TOKEN:number:( ["+"  "-"]?  ["0"-"9"]* "."  ["0"-"9"]*  "E"  ["+"  "-"] ["0"-"9"]+   )
07 TOKEN:entity_instance_name:( "#"["0"-"9"]["0"-"9" ]* "=" )
08 
09 COLOR:keyword: {
10     foreground_color: "#804000";
11     font_type:"bold";
12 }
13 
14 SKIP:whitespace
15 SKIP:block_comment
16 
17 S= <ISO> <semicolon> HeaderSection  DataSection <END_ISO> <semicolon>;
18 
19 DataSection = <DATA> <semicolon> ( SimpleEntityInstance | ComplexEntityInstance  )+ <ENDSEC> <semicolon>;
20 SimpleEntityInstance = <entity_instance_name> <keyword> ParameterList <semicolon>;
21 
22 TypedParameter = <keyword> <openBrace> Parameter <closeBrace>;
23 ParameterList =  <openBrace>  (  Parameter | <comma> )* <closeBrace>;
24 Parameter = TypedParameter | UntypedParameter | ParameterList ;
25 
26 NAVIGATOR:SimpleEntityInstance: {
27     display_name: "$entity_instance_name$ $keyword$";
28     tooltip: "$entity_instance_name$ $keyword$ $ParameterList$ ;";
29     icon: "/org/netbeans/modules/languages/resources/variable.gif";
30 }
31 
32 FOLD:HeaderSection:"{ Header Section... }"
33 BRACE "(:)"
34 SELECTION ( ["a"-"z" "A"-"Z" "0"-"9" "_" "#"] )

Am Anfang war das Wort

Grundlage der Sprachdefinition sind die reservierten Wörter (Tokens) einer Sprache. Je nach Bedarf lassen sie sich über einfache Zeichenketten oder reguläre Ausdrücke festlegen (Tabelle 1). Auf dieser Basis entsteht die Grammatik, die definiert, wie sich die einzelnen Elemente gültig verschachteln lassen.

Tabelle 1:
Reguläre Ausdrücke in Schliemann

 
 
 

Definition

Beschreibung

“Zeichenkette”

Buchstabenfolge

[“A” “B” “C”]

Buchstabe A oder B oder C

[^”n”]

Alles außer dem Zeilenumbruch

[“A” – “Z”]

Alle Buchstaben A bis Z

[“+” “-“]?

Kann ein + oder – enthalten

[“0” – “9”]+

Eine oder mehrere Ziffern

[“0” – “9”]*

Keine oder mehrere Ziffern

Zeile 17 in Listing 1 definiert eine Toplevel-Grammatik, die »S« heißen muss und die für die ganze Datei gültig ist. Sie greift zuerst auf die in den Zeilen 1 und 2 definierten Tokens »<ISO>« und »<END_ISO>« zurück, mit denen eine gültige STEP-Datei beginnen und enden muss. Außerdem verzweigt sie weiter zu den Untergrammatiken »HeaderSection« und »DataSection«.

Auf Basis der Tokens definiert der Entwickler eine Grammatik. Die für die gesamte Datei gültige Toplevel-Grammatik »S« nutzt das bereits definierte Token »<ISO>« und Untergrammatiken. Wie an den Elementen »Parameter« und »ParameterList« zu sehen ist, dürfen sich Grammatiken auch gegenseitig quer durch die Hierarchie-Ebenen aufrufen. So sind verschachtelte Listen oder innere Klassen oder Methoden möglich.

Auf der Grundlage der Grammatik stellt der Editor die Struktur im Navigator dar. Das Tag »NAVIGATOR«, legt fest, wie Netbeans die Datei unterteilt. In Listing 1 greift es auf die bereits in der Grammatik definierte Entität »SimpleEntityInstance« zurück. In den geschweiften Klammern legt der Entwickler fest, wie die Nodes in der Baumansicht aussehen (»icon«) und heißen (»display_name«) und wie der Tooltipp-Text lautet (»tooltip«). Ähnlich sind die Anzeige von Syntaxfehlern (Tag »MARK«) und das Code-Folding (Tag »FOLD«) konfigurierbar.

Die internen Module für Shell oder Javascript basieren auf Schliemann. Sind dessen Möglichkeiten ausgeschöpft, lassen sich eigene Java-Funktionen aus der NBS-Datei aufrufen. Den Einstieg erleichtern die Tutorials [9] und [10].

Fazit

Das Kate-Syntax-Highlighting bildet logisch nachvollziehbar Grammatiken in einer übersichtlichen XML-Datei. Das Emacs-Highlighting erschließt sich wohl nur Lisp-Fans mühelos, dafür sind Erweiterungen der flotten Highlighting-Engine möglich. Das gilt auch für Netbeans/Schliemann, das sich zudem durch eine flachere Lernkurve auszeichnet.

Infos

[1] Thomas Fischbacher, “Lisp”: Linux-Magazin-Sonderheft 04/04, S. 104

[2] Kate-Synax-Highlighting: [http://kate-editor.org/downloads/syntax_highlighting]

[3] NXML-Mode von James Clark: [http://www.thaiopensource.com/nxml-mode/]

[4] Js2-Mode von Steve Yegge: [http://code.google.com/p/js2-mode/]

[5] Stefan Kamphausen, “Major Modes für (X)Emacs”: Linux-Magazin 08/07

[6] Font-Locking [http://www.gnu.org/software/emacs/manual/html_node/elisp/Font-Lock-Mode.html#Font-Lock-Mode]

[7] Diskussion von »font-lock-add-keywords« auf dem Emacswiki: [http://www.emacswiki.org/cgi-bin/wiki/AddKeywords]

[8] STEP-Dateiformat: [http://de.wikipedia.org/wiki/Standard_for_the_exchange_of_product_model_data]

[9] General-Language-Tutorial: [http://wiki.netbeans.org/GLFTutorial]

[10] Schliemann-Tutorial: [http://wiki.netbeans.org/Part21Schliemann]

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 5 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben