Autor Beitrag
Martok
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: So 25.02.07 16:21 
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:
ausblenden 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

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
Backslash
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 202

WIN XP
Delphi 5 Ent, Delphi 2005 Prof
BeitragVerfasst: 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.

ausblenden 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); // hier Befehl bzw. Kommando mit Parametern ausführen
     if Trim(ResultText) <> '' then
       ShowMessage(ResultText); // Ausgabetext anzeigen  
   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;

ausblenden 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.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
// Verbs
type
  TVerbType = (vtTab, vtGet, vtSet, vtNone);

// Parametertypen
type
  TParamType = (ptIndex, ptCount, ptContent, ptNone);

// Paramterrecord
type
  TVerbParam = record
    VP_ParamType : TParamType;
    VP_ValueStr  : string;  // wenn ParamType "ptContent", dann steht hier der Inhalt als String
    VP_ValueInt  : Integer; // wenn ParamType beispielsweise "ptCount", dann steht hier die Zahl
  end;

// Kommandorecord
type
  TCommandRecord = record
    CR_Verb   : array of TVerbType; // Typ des Kommandos
    CR_Params : array[0..10of TVerbParam; // Parameter als Array (hier beispielsweise maximal 10 Parameter)


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:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
  CommandRecord.CR_Verb := vtSet;
  SetLength(CommandRecord.CR_Params, 1);
  with CommandRecord.CR_Params[0do
  begin
    VP_ParamType := ptCount;
    VP_ValueStr  := '';
    VP_ValueInt  := 34// Beispielwert
  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:

ausblenden 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.

ausblenden 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); // Kommando an Funktion ExecuteTab zum Ausführen durchschleifen
    vtGet : ResultText := ExecuteGet(Command); // Kommando an Funktion ExecuteGet zum Ausführen durchschleifen
    vtSet : ExecuteSet(Command); // Kommando an Funktion ExecuteSet zum Ausführen durchschleifen
    vtNone : exit; // Funktion verlassen
  else
    // entspricht vtNone 
    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:

ausblenden 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); // Hinweis: Der Wert VP_ValueInt wurde im Parser bei ParseNextCommand ja bereits eingelesen
      ptCount : ResultText := IntToStr(Command.CR_Params[i].VP_ValueInt);
      ptContent : ResultText := Command.CR_Params[i].VP_ValueStr; // Hinweis: VP_ValueString wurde im Parser ja bereits eingelesen und muss hier nurnoch übergeben werden
      ptNone  : Exit;
    else
      // entspricht hier laut Definition ptNone
      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.
Martok Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Do 15.03.07 19:29 
Ich liebe es, wenn steinalte Threads ausgegraben werden...

Das wäre schon wieder viel zu komplizert. Ich müsste noch mal kurz reingucken, wie ich das jetzt genau gemacht hab, grade hab ich aber keine Zeit, heute Abend vielleicht.

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."