![]() |
Wer zeigt mir, wie ich parsen muss?
Hallo Wissende,
da ich immer wieder Probleme mit meinem eigenen CSS-Editor habe, will ich nun das System des Zerschneiden des Inhaltes von { bis } und dem restlichen Löschen nun ein Ende bereiten und richtig parsen, denn ich verspreche mir davon, dass es endlich besser wird. Wer von Euch hätte denn die Güte mir bei diesen ersten Schritte ne Krücke zu reichen? Ich gebe mal einfach folgenden CSS-Code vor, der sich in einem meiner SynEdits befinden würde und anschliessend geparst werden sollte... Zitat:
Also wer erbarmt sich und erklärt mir was es mit token und co auf sich hat, oder wie man dieses Thema mit reglären Ausdrücken "erledigen" kann. |
Re: Wer zeigt mir, wie ich parsen muss?
Oje... mal sehen (Kurzform):
1. Ein 'Scanner' zerlegt die Eingabe in Token. 2. Ein 'Token' ist die Grundeinheit oder ein 'Wort' der zu parsenden Sprache. 3. Ein Parser implementiert die Grammatik der zu parsenden Sprache. So einfach ist das. Bleiben nur noch 3 Kleinigkeiten: 1. Wie schreibt man einen Scanner? 2. Was ist denn eine Grammatik? 3. Und wie implementiert man dann den Parsers? Ein Scanner liefert also die Wörter des Inputs. Für jedes Wort kann man einen regulären Ausdruck angeben, der genau dieses Wort beschreibt. Den Scanner kann man mit einem 'Deterministischen Endichen Automaten' (DEA) implementieren (reguläre Ausdrücke sind selbst DEA ;-)), der Zeichen für Zeichen gegen die regulären Ausdrücke prüft und bei Erkennen eines vollständigen Wortes dieses 'Token' an den Parser übergibt. [Edit] Der Kommentar wird z.B. von Scanner erkannt und als 'white Space' an den Parser übergeben [/edit] Der Parses muss nun noch prüfen, ob die Token-Folge der zu prüfenden Sprache genügt. Dabei erstellt er zwangsweise einen Syntaxbaum, der dann, sozusagen 'en passant' auch die von Dir gewünschte Information enthält. Wie Du siehst, kommen mit jeder Erklärung neue Begriffe hinzu, was den Schluss zulässt, das 'Parsen' und 'Scannen' nicht so einfach sind. Also, einfach ist es schon, aber nicht trivial. Na ja, trivial ist es auch. Wenn man es kann ;-) Sooo schwer ist das allerdings nicht. Wenn Du einen Scanner geschrieben hast, dann besorge Dir erstmal eine Definition der Sprache, am besten in Backus-Naur-Form. Denn diese Form hilft Dir ungemein bei der Implementierung eines prozeduralen Parsers, der bei einfachen Sprachen am Schnellsten zu implementieren ist. Hier im Forum habe ich aber schon fertige 'Scanner' und auch einige Artikel über 'Parser' gesehen, sodaß Dir die Foren-Suche weiterhelfen sollte. |
Re: Wer zeigt mir, wie ich parsen muss?
Hm, also das scheint mir doch relativ komplex zu sein. Aber um vielleicht nochmal zur Praxis zurückzukommen... Wie könnte ich es denn Codetechnisch angehen?
Ich habe sicher nicht alles von dem verstanden, was Du geschrieben hast, habe auch diverse Google-Pages durchgelesen, aber auch hier im Forum kein wirkliches Codebeispiel finden können. Mir ist schon klar, dass ich nichts für mir sofort brauchbares finden kann, da es hier sicher noch keinen Scanner für CSS, so ich das denn richtig verstanden habe, gibt, aber wie wäre es denn mit einem kleinen Beispiel für Scanner, Grammatikfestlegung und Parser...? So wie ich es verstanden habe, findet der Scanenr nur die Worte. Das wären, um bei meinem Beispiel zu bleiben folgende: body, font-family,verdana,font,size,12px /* Dann ein Kommentar zum üben */ .class, #id ,color, green, border, 1px, solid, gray, Das wäre doch im Prinzip alles ausser , ; : (Komma, Semikolon und Doppelpunkt) Wie würde aber das mit der lexikalischen Analyse funktionieren oder wie wäre das mit den regulären Ausdrücken. Das sind alles so unklare Formulierungen. Ich bin da immer der Freund des praktischen Beispieles, weil da wohl meine Vorstellungskraft nicht ausreicht. Im Prinzip habe ich es bisher wie folgt gemacht.
Delphi-Quellcode:
Leider ist diese Routine nicht so perfekt, hier fehlt auch noch diverses Zeugs, was zeigt, wie es weitergeht mit dem Code. Eines meiner Hauptprobleme ist, dass parsen während der Codeeingabe. Da geht mir immer wieder der Fokus von der SynEdit verloren, so dass man mitten im Schreiben einfach nicht weiter arbeiten kann.
procedure ReadElemente;
var Stringliste:TStringList; Pos_A,Pos_E:integer; Element,Inhalt:String; begin ParserError:=False; //erstmal alles leeren myClass.Clear; myHTML.Clear; myIndi.Clear; //der inahlt dieser stringlisten wir in einen Treeview geschrieben, //wo man dann zugeordnet sehen kann, welche klassen, id´s oder html-elemende man hat myTClass.Clear; myTHTML.Clear; myTIndi.Clear; Stringliste:=TStringList.Create; Stringliste.AddStrings(MainForm.SynEdit1.Lines); //holt inhalt vom synedit if MainForm.SynEdit1.Lines.Count > 0 then Inhalt:=ClearCommentarTStrings(Stringliste); //löscht die kommentare while length(Inhalt) > 0 do //zerkaut den inhalt bis keiner mehr da ist begin {und hier das abarbeiten des Codes} Pos_A:= pos('{',Inhalt); //begin des formatierten elementes gefunden if Pos_A = 0 then begin Inhalt := ''; Stringliste.Free; ParserError:=True; exit; end; Pos_E:= pos('}',Inhalt); //ende des formatierten elementes gefunden if Pos_E > 0 then begin //ermittelt den elementnamen Element:=Trim(midstr(Inhalt,0,Pos_A-1)); //ShowMessage(Element); //ermittelt den elementtyp //hier klasse if (leftstr(Element,1) = '.') or (pos('.',Element) > 1) then begin myClass.Add(Element); end //hier ID else if (leftstr(Element,1) = '#') or (pos('#',Element) > 1) then begin myIndi.Add(Element); end else begin //ansonsten HTML-Element myHTML.Add(Element); end; //löscht das element mit allen angaben aus der liste Delete(Inhalt,1,Pos_E); Stringliste.Text:=Inhalt; end else begin ParserError:=True; //setzt globalen bool exit; end; end; Stringliste.Free; end; |
Re: Wer zeigt mir, wie ich parsen muss?
Moin Tom,
also ich würde es, stilisiert, so angehen:
Delphi-Quellcode:
Das nur einmal als grobes Beispiel. Das Auslesen eines Bezeichners sollte auch nicht direkt in der Schleife geschehen, sondern als Funktion ausgelagert werden, die, beispielsweise, als DEA ausgeführt ist, um einen gültigen Bezeichner zu extrahieren.
while i <= length(sBuffer) do begin
case sBuffer[i] of 'a'..'z' : begin // Bezeichner möglich // alle Zeichen aus dem Buffer auslesen, bis zum ersten Auftreten // eines Zeichens, dass nicht in einem Bezeichner vorkommen kann // Hierbei i entsprechend hochzählen (muss am Ende den Index des letzten // gültigen Zeichens enthalten // Jetzt kann geprüft werden, ob es sich nun um ein Keyword oder eine Variable // handelt, und der Wert entsprechend weggeschrieben werden. end; ';',',' : begin // Delimiter // Kann direkt als Token geschrieben werden end; '/' : begin // Evtl. beginnt hier ein Kommentar if i < length(sBuffer) then begin // folgt ein * ? // Ja, dann bis */ suchen, sonst direkt / übernehmen end else begin // i = Bufferlänge => Es kann kein Kommentar sein end; end; #32,#10,#13,#09 :; // Whitespace, es gibt nichts zu tun end; inc(i); end; Unter dem Begriff "Zustandsautomat" kannst Du auch einiges als Erklärung für einen DEA finden. Vielleicht kannst Du für CSS auch so etwas finden, wie die Referenz für C bzw. C++, z.B. bei MS. Dort werden die verschiedenen Phasen eines Compilerdurchlaufes in Einzelheiten beschrieben. BTW: Ein Token setzt sich, i.d.R., aus zwei Teilen zusammen, nämlich Symbol und Attribut, wobei letzteres nur bei Variablen Werten erforderlich ist. Beispielweise könnte das Token für Addition als (+,) dargestellt werden, da hier nur das Symbol interessiert, wohingegen das Token für eine Variable als (var,MyVar) dargestellt werden kann, wobei var das Symbol für eine Variable im allgemeinen steht, und MyVar den Namen des Bezeichners angibt. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:04 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