Open Source im professionellen Einsatz
Linux-Magazin 04/2005
476

Einfacher Parser

Der Beispielparser soll Eingabedateien verarbeiten, die wie Listing 1 aussehen (natürlich ohne Zeilennummerierung). Das Format kennt drei optionale Schlüsselwörter: »anrede«, »vorname« und »nachname«. Leerzeichen und -zeilen sind ebenfalls optional, außer sie treten als Bestandteil der Werte auf wie etwa in »anrede = Herr Dr. Dr.«.

Listing 2 spezifiziert die Grammatik im JavaCC-Format. Am Anfang stehen Optionen für den Parsergenerator, die sich auch über Kommandozeilenargumente setzen lassen. Während der Grammatikentwicklung ist es sehr nützlich, den Debug-Modus anzuschalten. Das Flag »DEBUG_PARSER« produziert vergleichsweise wenig Ausgaben, »DEBUG _TOKEN_MANAGER« ist dagegen sehr geschwätzig.

Die eigentliche Parserdefinition steht zwischen den Zeilen 27 (»PARSER_BEGIN«) und 38 (»PARSER_END«). Der Name nach »PARSER_BEGIN« legt fest, wie die generierten Datei- und Klassennamen lauten, in diesem Fall »HelloWorld.java«, »HelloWorldTokenManager .java« und »HelloWorldConstants.java«. Zwischen »PARSER_BEGIN« und »PARSER_END« kann beliebiger Java-Code stehen, solange dort eine Klassendefinition für eine Klasse mit dem angegebenen Namen vorhanden ist.

Listing 1:
Eingabedatei

01 anrede = Herr
02 vorname=Bernhard
03 nachname = Bablok

Listing 2: Die Grammatik
»HelloWorld.jj«

22 options {
23   DEBUG_PARSER = true;
24   DEBUG_TOKEN_MANAGER = false;
25 }
26
27 PARSER_BEGIN(HelloWorld)
28
29 package hello;
30
31 public class HelloWorld {
32   public static void main(String args[]) throws ParseException {
33     HelloWorld parser = new HelloWorld(System.in);
34     parser.processInput();
35   }
36 }
37
38 PARSER_END(HelloWorld)
39
40 <DEFAULT> SKIP :
41 {
42   " "
43 | "t"
44 | "n"
45 | "r"
46 }
47
48 <DEFAULT> TOKEN :
49 {
50   < EQ: "="(" ")* >: VALUE
51 | < FIRSTNAME: "Firstname" | "firstname" | "f" >
52 | < LASTNAME: "Lastname" | "lastname" | "l" >
53 | < TITLE: "Title" | "title" | "a" >
54 }
55
56 <VALUE> TOKEN:
57 {
58  < STRING_LITERAL: (~["n","r"])*>:DEFAULT
59 }
60
61 void processInput() :
62 { }
63 {
64   (keyspec())*
65 }
66
67 void keyspec() :
68 {
69 }
70 {
71   titleSpec() | firstnameSpec() |  lastnameSpec()
72 }
73
74 void titleSpec() :
75 { }
76 {
77   <TITLE> <EQ> <STRING_LITERAL>
78 }
79
80 void firstnameSpec() :
81 { }
82 {
83   <FIRSTNAME> <EQ> <STRING_LITERAL>
84 }
85
86 void lastnameSpec() :
87 { }
88 {
89   <LASTNAME> <EQ> <STRING_LITERAL>
90 }

Token und lexikalischer Status

Nach der grundsätzlichen Parserdefinition folgen die Produktionen. Im Beispiel sind dies zunächst drei so genannte Regular Expression Productions der Typen »SKIP« und »TOKEN«. Wie der englische Name Skip (überspringen) nahe legt, definiert diese Produktion Zeichen, die der Parser ignoriert. Das Symbol »TOKEN« legt Einheiten fest, die der Tokenizer als Token erkennt.

Wie andere Lexer unterstützt auch JavaCC mehrere Zustände für den lexikalischen Status. Im Beispiel gibt es zwei: »DEFAULT« und »VALUE«. Damit unterscheidet der Lexer zwischen dem »=«-Zeichen als lexikalischem Element (das für den Parser von Bedeutung ist) und einem »STRING_LITERAL« (das der Parser unbesehen übernimmt).

In Zeile 50 von Listing 2 ist das »EQ«-Token definiert. Das Suffix »: VALUE« zeigt an, dass der Lexer nach diesem Token in den Status »VALUE« gehen soll. Analog dazu wechselt der Lexer nach einem »STRING_LITERAL« zum Status »DEFAULT« zurück (Zeile 58).

Linux-Magazin kaufen

Einzelne Ausgabe
 
Abonnements
 
TABLET & SMARTPHONE APPS
Bald erhältlich
Get it on Google Play

Deutschland

Ähnliche Artikel

  • Wir können auch anders

    Bisher vorgestellte Programme waren in funktionaler Weise implementiert. Schemes Stärken liegen zwar hauptsächlich in diesem Bereich, jedoch gibt es auch die Möglichkeit, objektorientiert vorzugehen.

  • Hello, World!

    Programmiersprachen, Bibliotheken, Tools, Entwicklungsumgebungen: Das Linux-Magazin widmet sich in einer eigenen Rubrik den Fragen rund ums Programmieren.

  • Haskell

    Haskell ist keine Skriptsprache, sondern wird in der Regel kompiliert. Mit einigen Handgriffen lassen sich aber Shellskripte in die funktionale Sprache einbinden. Haskells starkes Typensystem greift dann ein, um Fehler durch Argumente in falscher Anzahl oder Art zu verhindern.

  • Shell scripting with type-safety using Haskell

    Why is scripting usually done in dynamically typed languages? This article applies strong typing in Haskell to shell programs. The end result can still be light-weight but also save time by reducing runtime errors.

  • Übersetzungskünstler

    Lexer und Parser in Perl schreiben ist keineswegs langbärtigen Gurus vorbehalten. Am Beispiel eines mathematischen Formelparsers nimmt dieser Perl-Snapshot die Angst vor Token und RPN.

comments powered by Disqus

Ausgabe 11/2017

Digitale Ausgabe: Preis € 6,40
(inkl. 19% MwSt.)

Stellenmarkt

Artikelserien und interessante Workshops aus dem Magazin können Sie hier als Bundle erwerben.