Martok - So 25.02.07 16:21
Titel: Befehlsstrings verarbeiten
Also, mal wieder was theoretisches von mir ;)
Und zwar habe ich eine Pluginschnittstelle (mehr oder weniger Skript-artig), die auf CommandStrings basiert. Diese sehen z.B. so aus:
Category.Item.Function(Param1,Param2,...,ParamN)Nun muss ich das Parsen, und dann darauf reagieren. Das Parsen ist kein Problem, da kommt Pos/Copy zum Einsatz. Problematisch wird es, wie ich die Daten am besten
1. Ablege. String-Variablen, Record (TCommand), Parameter als Array?
2. In Befehle übersetze. In einer etwas anderen Version der Schnittstelle, die ich genau deswegen umschreibe, sieht das so aus:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| if verb1='tab' then begin if verb2='get' then begin if prm1='index' then ResultText:= IntToStr(PageControl1.ActivePage.PageIndex); if prm1='count' then ResultText:= IntToStr(PageControl1.PageCount); if prm1='content'then ResultText:= ActEdit.Text; end; if verb2='set' then begin if prm1='index' then SelectPage(StrToInt(prm2)); if prm1='content'then ActEdit.Text:= prm2; end; end; |
Man sieht: es ist eine Baumartige Kette von If-Abfragen. Nur ist das 1. unübersichtlich und 2. ganz schlecht zu erweitern, daher die neue Struktur. Ganz überfahren könnte ich einen Pas-Parser nehmen, aber dass muss dann doch nicht sein ;)
An dem Problem hab ich jetzt seit gestern geknobelt, aber mir fällt einfach keine gute Herangehensweise ein.
Falls mir hier einer Helfen könnte, wäre ich euch sehr verbunden ;)
Danke,
Sebastian
Backslash - Do 15.03.07 15:55
Da bisher niemand auf deinen Thread geantwortet hat, will ich mich mal an einem Lösungsansatz versuchen. Wie ich sehe, arbeitest du mit Verbs und Parametern die du dynamisch (wahrscheinlich für jede Befehlszeile) parst. Da ich jetzt davon ausgehe, dass du auch mehr wie 2 Verben haben kannst, würde ich die Sache folgendermaßen angehen.
1. Du hast eine Funktion die die Verben und Parameter aus einem Kommando deiner zu parsenden Codedatei einliest.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| var Line : Integer; CommandRecord : TCommandRecord; ResultText : String; begin Line := 0; while ParseNextCommand(Line, CommandRecord) do begin ExecuteCommand(CommandRecord, ResultText); if Trim(ResultText) <> '' then ShowMessage(ResultText); end; end; |
Erklärungen:
ParseNextCommand: Diese Routine liefert dir einen Record zurück, indem das aktuell ausgelesene Kommando und die Parameter deklariert sind.
Übergeben tust du die aktuell zu lesende Zeile (Line), und zwar als var Parameter. Intern erhöht die Parsingfunktion zum Auslesen der Kommandoparameter die Zeile um 1.
Als zweiten Paramter übergibst du den Kommandorecord ebenfalls als var Parameter.
Hat die Funktion ParseNextCommand ein Kommando gefunden, gibt sie True zurück. Findet sie beispielsweise nur eine Leerzeile, erhöht sie intern so lange die Zeile, bis das nächste Kommando gefunden wird.
Prototyp der Funktion ParseNextCommand;
Delphi-Quelltext
1:
| function ParseNextCommand(var Line : Integer; out CommandRecord : TCommandRecord) : Boolean; |
CommandRecord:
CommandRecord ist hier ein selbst definierter Record, der beispielsweise so aussehen könnte.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21:
| type TVerbType = (vtTab, vtGet, vtSet, vtNone);
type TParamType = (ptIndex, ptCount, ptContent, ptNone);
type TVerbParam = record VP_ParamType : TParamType; VP_ValueStr : string; VP_ValueInt : Integer; end;
type TCommandRecord = record CR_Verb : array of TVerbType; CR_Params : array[0..10] of TVerbParam; |
WICHTIG: Ich habe hier mit Enumerationen gearbeitet. Die Funktion ParseCommands muss natürlich mit einer Stringpass-Routine die entsprechenden Schlüsselstrings wie "tab", "get", "index", etc. auslesen und dann den Kommandorecord entsprechend initialisieren.
Beispiel:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| CommandRecord.CR_Verb := vtSet; SetLength(CommandRecord.CR_Params, 1); with CommandRecord.CR_Params[0] do begin VP_ParamType := ptCount; VP_ValueStr := ''; VP_ValueInt := 34; end; |
Dieser Kommandorecord wird in der Funktion ExecuteCommand verarbeitet.
ExecuteCommand:
Diese Funktion bzw. Prozedur verarbeitet ein Kommando. Wahlweise kann noch ein Rückgabewert für einen Successstate eingefügt werden.
Prototyp:
Delphi-Quelltext
1:
| procedure ExecuteCommand(var Command : TCommandRecord; out ResultText : string); |
Hinweis: ResultText wird hier einfach in der Funktion ExecuteCommand(Command, ResultText); durchgeschleift.
Das Abarbeiten der Kommandos in der Funktion sieht relativ simple aus. Es wird mit einer Case-Anweisung geprüft, um welches Verb es sich handelt und dann einfach in die entsprechende Funktion zum Ausführen des Kommandos verzweigt.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| procedure ExecuteCommand(var Command : TCommandRecord; out ResultText : string); begin ResultText := ''; case Command.CR_Verb of vtTab : ExecuteTab(Command); vtGet : ResultText := ExecuteGet(Command); vtSet : ExecuteSet(Command); vtNone : exit; else Exit; end; end; |
ExecuteGet
In der Prozedur ExecuteGet wird weiter verzweigt und der ResultString als Funktionsresult zurückgegeben. Die Implementierung sieht beispielsweise so aus:
Implementierung:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| ExecuteGet(var Command : TCommandRecord; out ResultText : string); var i : Integer; begin ResultText := ''; for i := 0 to 9 do begin case Command.CR_Params[i].VP_ParamType of ptIndex : ResultText := IntToStr(Command.CR_Params[i].VP_ValueInt); ptCount : ResultText := IntToStr(Command.CR_Params[i].VP_ValueInt); ptContent : ResultText := Command.CR_Params[i].VP_ValueStr; ptNone : Exit; else Exit; end; end; end; |
[Abschließend]
Das wars soweit vom Prinzip hier. Der Parser muss nurnoch beim Lesen für jedes Kommando die Einzelparameter in den Commandrecord packen und nur auf den Datentyp achten (Integer, String, etc.). Das ganze wird solange eingelesen, bis ein ";" kommt und damit ersichtlich ist, dass die Kommandozeile abgeschlossen ist. Aufgezweigt wird intern.
Viele Grüße
Backslash
PS: Das war soweit mein Lösungsansatz. In der Art würde ich da rangehen.