Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi Parserbau Grundlagen + Verständnisfrage (https://www.delphipraxis.net/170551-parserbau-grundlagen-verstaendnisfrage.html)

Nintendo 22. Sep 2012 17:41

Parserbau Grundlagen + Verständnisfrage
 
Hallo,

ich versuche mich grad an einem Parser, der wie folgt aufgebaut ist:

Delphi-Quellcode:
procedure Parse;

begin
  while LineNum < Stringliste.Count do
  begin
    Token := GetToken;
    //LineNum wird in SkipSpaces erhöht, wenn Zeile zuende
    SkipSpaces;
    if Token = 'Mein_gesuchtes_Wort' then
    begin
      Token := GetToken;
      SkipSpaces;
      if Token = 'Mein_nächstes_gesuchtes_Wort' then
      begin
        //entweder nächsten Token holen oder, wenn fertig
        //Aktion auslösen
      end;
    end;
  end;
end;

//Hier noch die Prozedur SkipSpaces

procedure SkipSpaces;
begin
  while StringListe.Strings[LineNum][StringPosition]=' ' do inc(StringPosition);
  if StringPosition >= Length(StringListe.Strings[LineNum]) then
  begin
    StringPosition := 1;
    inc(LineNum);
  end;
end;
Leider kommt mein Parser aber nicht zu '''if Token='Mein_Nächstes_gesuchtes_wort' then ...

Warum nicht?

In meinem Testtext kommen die gesuchten Wörter definitiv mit Sicherheit vor.

Warum findet sie der Parser dann nicht?

Bisher habe ich rausgefunden, das der Parser scheitert, wenn Leerzeilen dazwischen liegen, denn dann läuft er, findet das nächste gesuchte Wort nicht, die Schlife läuft bis zum Ende und im nächsten Parserdurchlauf ist er nicht mehr in jenem Scope, wo ich das 'mein_nächstes_gesuchtes_wort erwarte. ER ist stattdessen in der Ebene, wo er höchstens 'mein_gesuchtes_wort' finden würde, das aber im Text dann nicht mehr vorkommt.

Wie kann ich den Entwurf besser machen, so das Spaces einfach überlesen werden.

Uwe Raabe 22. Sep 2012 17:52

AW: Parserbau Grundlagen + Verständnisfrage
 
SkipSpaces überspringt das letzte Zeichen in einer Zeile, wenn es kein Space ist. Wenn eine Zeile mit einem Leerzeichen anfängt, hast du auch ein Problem.

implementation 22. Sep 2012 18:50

AW: Parserbau Grundlagen + Verständnisfrage
 
Du solltest SkipSpaces nochmal überdenken.
Das Problem wird sichtbar, wenn du die Unteraufgaben noch ein wenig auslagerst. Du möchtest es sicherlich etwa so haben:
Delphi-Quellcode:
procedure SkipSpaces;
begin
  while (Look()=' ') or IsLinebreak() do
    MoveForward();
end;
So, wie du es jetzt geschrieben hast, sieht es aber so aus:
Delphi-Quellcode:
procedure SkipSpaces;
begin
  while Look()=' ' do
    MoveForward();
  if IsLinebreak() then
    MoveForward();
end;
Siehst du den Unterschied?


Tipp am Rande: Du solltest dir die wichtigen Subaufgaben sowieso besser auslagern. Ein
Delphi-Quellcode:
Look()
/
Delphi-Quellcode:
Peek()
und ein
Delphi-Quellcode:
MoveForward
wirst du noch an vielen Stellen brauchen. Und noch ein Tipp: Implementier Look()/Peek() so, dass es Zeilenumbrüche selbst behandelt und als #10 oder #13 zurückliefert, das macht vieles einfacher.

Furtbichler 23. Sep 2012 10:22

AW: Parserbau Grundlagen + Verständnisfrage
 
Ich würde noch den zu parsenden Text nicht als Liste von Zeilen ansehen, sondern als einen einzigen String. Den Zeilenvorschub kannst Du als Leerzeichen behandeln (beim einlesen). Dann ist alles viel transparenter und dein Skipspaces wird zu einem einfachen

Delphi-Quellcode:
Procedure SkipSpaces;
Begin
  While (TextPos<Length(MyText)) and (MyText[TextPos] in WhiteSpaceCharacters) do
    inc(TextPos);

  If TextPos>Length(MyTExt) Then
    TextIsAtEof := True;
End;
Noch viel transparenter wird es so (finde ich)
Delphi-Quellcode:
Procedure Advance;
Begin
  if TextisAtEof Then
    Raise EParserException.Create('Tried to read past eof');
  inc(TextPos);
  If TextPos>Length(MyTExt) Then
    TextIsAtEof := True;
End;

Procedure SkipSpaces;
Begin
  While not TextIsAtEof and (MyText[TextPos] in WhiteSpaceCharacters) do
    inc(TextPos);
End;
Das saubere 'Advance' kannst Du auch in deinem 'GetToken' verwenden.

Nintendo 24. Sep 2012 20:16

AW: Parserbau Grundlagen + Verständnisfrage
 
Hey, danke Euch allen für Eure Antworten. Habe jetzt folgende Version für SkipSpaces:

Delphi-Quellcode:
procedure MoveForward;
begin
  while FLook=' ' do FLook:=GetChar;
  if IsNewLine(FLook) then FLook:=GetChar;
end;

//Und nun meine Version für GetToken:

function GetToken: String;
begin
 FLineNum := 0;
 while FLinenum < FParseThis.Count do
 begin
  FPos := 1;
  while FPos<=Length(FParseThis[FLineNum]) do
  begin
    MoveForward;
    FLook := GetChar;       //nächstes Zeichen
    Token := Token + Flook; //Token bauen
    inc(FPos);              //Zeichenposition anpassen
  end;
  inc(FLineNum);
 end;
 Result := FToken;
 DoGetToken(FToken);
end;
Nun gerät GetToken aber in eine Endlossschleife. Warum das jetzt?

Furtbichler 24. Sep 2012 20:38

AW: Parserbau Grundlagen + Verständnisfrage
 
Ich glaube, Du hast einen Denkfehler.

Verwende drei Operationen:
GetChar:
Liefert das nächste Zeichen. Bei Zeilenende liefert die Funktion ein ' '. Bei TAB auch.
Wenn es den Beginn eines Kommentars sieht (Lookahead!) dann überspringt die Funktion alles bis zum Ende des Kommentars.
Anstelle des Kommentars wird ein ' ' geliefert.


SkipSpaces:
Geht bis zum nächsten Zeichen <> ' '. Verwendet GetChar.

GetToken:
Liefert einen String bestehen aus den Zeichen bis zum nächsten Terminalsymbol.
Danach mit SkipSpaces weiterhüpfen.

Das reicht.

Delphi-Quellcode:
Function TwoWords (ParserTree : TParserTree) : Boolean;
Begin
  if GetToken.TokenType = tkWord then begin
    // Remember current word;
    If GetToken.TokenType = tkWord then begin
       // Success - we've found two words in a row
       ...
       exit;
    end;
    Raise ParserError('Expected a word here')
  end;
  Raise ParserError('Expected a word here')
End;
Du bist beim Parsen etwas eingeschränkt, weil Du den Parser mit den paar Operationen nicht dazu bringen kannst, nach einem Syntaxfehler wieder so aufzusetzen, das alle Fehler gefunden werden. Aber das ist -soweit ich mich entsinne- eh nicht trivial.


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:30 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz