![]() |
|||
| HSG |
|
Vereinfacht gesagt, muss ein Bezeichner mit einem Buchstaben oder Unterstrich beginnen. Optional dürfen Buchstaben, Unterstrich oder Ziffern folgen.
Wenn b für einen Buchstaben oder Unterstrich und z für eine Ziffer steht, so kann man leicht einen Akzeptor für einen Bezeichner angeben:

Dabei gilt die übliche Konvention, dass der Fehlerzustand nicht explizit angegeben wird.
Es typisch, dass bei der Realisierung eines abstrakten Automaten Zeichen erst Zeichenklassen zugeordnet werden müssen. Das kann z.B. über Mengen geschehen.
Der Akzeptor wird getestet.
Folgende Grammatik beschreibt eine Delphi-Fließkomma-Zahl.
Wir nehmen als eine einfache Sprache L durch Whitespaces getrennte Bezeichner und pointfloats an. Ein Scanner soll nun aus einer Zeichenkette die Tokens Bezeichner und pointfloat extrahieren bzw. einen Fehler anzeigen, wenn dies nicht mehr möglich ist. Sicher werden die bereits erstellten Automaten nützlich sein. Trotzdem ist die Aufgabe insofern anders als eine Zeichenkette, die z.B. als Bezeichner erkannt wurde, gespeichert werden soll. Außerdem soll der Scanner nach Erkennen eines Tokens weiterarbeiten.
NEA
DEA
spezieller DEA
Der letzte Automat arbeitet nun so: Ein Puffer nimmt die verarbeiteten Zeichen auf. Sobald einer der Endzustände über eine Transition mit Stern verlassen wird, wird ein Token bestehend aus Endzustand und Puffer gespeichert. Dabei gehört das Zeichen, das zum Verlassen geführt hat, nicht mehr zum Puffer. Dieses Zeichen wird vielmehr für einen neuen Start aus dem Startzustand benutzt. Gerät man in den Fehlerzustand, so wird ein Fehler-Token gespeichert. Ist man nach dem letzten Zeichen in einem Endzustand, so wird das entsprechende Token gespeichert, sonst ein Unerwartetes-Ende-Token.
Der Scanner kann Bezeichner, Zahlen und Whitespaces unterscheiden. Es fehlt noch eine genaue Beschreibung des Vorgehens!

procedure TForm1.bScanClick(Sender: TObject);
type
TZustand = (q0,ws,bez,z1,ko,z2,f);
TEingabe = (w,b,z,k,sonst);
const
Zeichen : array[TEingabe] of set of char =
( [' ',#10,#13],
['A'..'Z']+['a'..'z'],
['0'..'9'],
[','],
[chr(0)..chr(255)]-[' ',#10,#13]-['A'..'Z']-['a'..'z']-['0'..'9']-[',']);
startzustand = q0;
Endzustaende : set of TZustand = [ws,bez,z1,z2];
fue : array[TZustand,TEingabe] of TZustand =
((ws,bez,z1,f,f), // q0
(ws,q0,q0,f,f), // ws
(q0,bez,bez,f,f), // bez
(q0,q0,z1,ko,f), // z1
(f,f,z2,f,f), // k
(q0,q0,z2,f,f), // z2
(f,f,f,f,f)); // f
(*
die Tabelle ist so aufgebaut:
( (alle Folgezustände zum ersten Zustand q0,
geordnet nach den möglichen Eingaben),
(alle Folgezustände zum zweiten Zustand ws),
....
(alle Folgezustände zum letzten Zustand f) )
*)
var
s,puffer : string;
zustand,zustand_alt : TZustand;
eingabe,ein : TEingabe;
i : integer; // Zeiger auf aktuelles Zeichen
procedure verarbeite_ws; // wird am Ende von Whitespaces aufgerufen
begin
puffer := '';
i := i-1;
end;
procedure verarbeite_z; // wird am Ende einer Zahl aufgerufen
var
wert : string;
begin
wert := copy(puffer,1,length(puffer)-1);
mAus.Lines.Add('Zahl '+wert);
puffer := '';
i := i-1;
end;
procedure verarbeite_bez; // wird am Ende eines Bezeichners aufgerufen
var
wert : string;
begin
wert := copy(puffer,1,length(puffer)-1);
mAus.Lines.Add('Bezeichner '+wert);
puffer := '';
i := i-1; // Zeiger zurücksetzen
end;
procedure verarbeite_f; // wird im Fehlerfall aufgerufen
begin
mAus.Lines.Add('Fehler und Abbruch!');
i := Length(s);
end;
begin
// Ausgabe löschen
mAus.Lines.Clear;
// Eingabestring setzen
s := mEin.Text;
// Startzustand setzen
zustand := startzustand;
// Puffer zu Beginn leer
puffer := '';
// alle Eingabezeichen durchgehen
i := 1;
while i <= Length(s) do
begin
// Zeichen anhängen
puffer := puffer+s[i];
// Ermittlung der Eingabe
for ein := low(TEingabe) to high(TEingabe) do
if s[i] in Zeichen[ein] then eingabe := ein;
// alten Zustand speichern
zustand_alt := zustand;
// neuer Zustand
zustand := fue[zustand,eingabe];
// auf Ereignisse testen und reagieren
if zustand = f
then
verarbeite_f
else
if (zustand = q0) and (zustand_alt in Endzustaende)
then
case zustand_alt of
ws : verarbeite_ws;
bez : verarbeite_bez;
z1,z2 : verarbeite_z;
end // of case
else
if (i = Length(s)) and (zustand in Endzustaende)
then
begin
case zustand of
ws : verarbeite_ws;
bez : verarbeite_bez;
z1,z2 : verarbeite_z;
end; // of case
i := i+1; // damit es zum Abbruch kommt
end;
// ein Zeichen weiter
i := i+1;
end;
end;
Erweitere den Scanner um das Überlesen von Kommentaren.