Entwickler-Ecke
Sonstiges (Delphi) - Befehlsinterpreter - goto - wie springen?
galagher - Di 19.05.09 17:35
Titel: Befehlsinterpreter - goto - wie springen?
Hallo!
Ich bin dabei, einen Befehlsinterpreter wie COMMAND.COM (nur besser :wink: ) zu schreiben, und nun hänge ich beim
goto.
Das Prinzip ist einfach: Ich lade die abzuarbeitende Datei in eine TStringList und gehe Zeile für Zeile durch:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| echo 1 goto END echo 2 echo 3
:END echo end |
Das Ganze läuft in einer for-Schleife. Daher ist es einfach, zeilenweise zu arbeiten, aber wie stellt man den for-Zähler höher? Wie könnte man das machen?
jaenicke - Di 19.05.09 17:40
Den Schleifenzähler verändern ist nicht möglich. Und dein Konzept mit Goto sieht auch sehr sehr unübersichtlich und vermutlich unnötig langsam aus.
Was du machen kannst ist mit Continue zum nächsten Durchlauf der Schleife zu gehen, also den Rest des aktuellen Durchlaufs zu überspringen.
galagher - Di 19.05.09 17:53
jaenicke hat folgendes geschrieben : |
| Den Schleifenzähler verändern ist nicht möglich. |
Ich weiss. Das ist es ja - man braucht irgendwie einen zweiten Zähler...
jaenicke hat folgendes geschrieben : |
| Und dein Konzept mit Goto sieht auch sehr sehr unübersichtlich und vermutlich unnötig langsam aus. |
In Stapeldateien gibt es aber nun mal goto, und - na klar ist das unübersichtlich! Und was langsam betrifft - bei meinetwegen 20 Zeilen Stapeldatei-Code ist was wohl egal! Abgesehen davon, interpretiert mein Programm sowieso jeden Befehl, und interpretierter Code ist immer langsam(er)!
Ich meine das so:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23:
| for i := 0 to wcdList.Count-1 do begin try if not (wcdList[i] = '') then begin if not (wcdList[i][1] = ';') and not (GetWord(LowerCase(wcdList[i]), 1) = 'rem') and not (wcdList[iwcdline][1] = ':') then RxRichEdit1.Lines.Add(wcdList[i]);
if not (wcdList[i][1] = ':') then CommandExecute(wcdList[i]); end;
except ErrorText(sGeneralError); break; end; end; |
jaenicke hat folgendes geschrieben : |
| Was du machen kannst ist mit Continue zum nächsten Durchlauf der Schleife zu gehen, also den Rest des aktuellen Durchlaufs zu überspringen. |
Habe continue bisher nicht mal gekannt...
jaenicke - Di 19.05.09 17:57
galagher hat folgendes geschrieben : |
| In Stapeldateien gibt es aber nun mal goto |
:oops:
Ich hatte das goto als dein Vorhaben zur Umsetzung in Delphi verstanden. Dass es in Batchdateien benutzt wird, ist klar. Tut mir leid.
Was das Problem betrifft:
Wie wäre es mit einer while-Schleife? ;-)
Da kannst du den Schleifenzähler beliebig verändern, wobei das dann auch kein Zähler, sondern ein Zeiger auf die aktuelle Zeile wäre. Und genau das brauchst du ja.
Und sobald du über die letzte Zeile hinaus bist, bricht die Schleife ab.
Dunkel - Di 19.05.09 17:59
jaenicke hat folgendes geschrieben : |
| Den Schleifenzähler verändern ist nicht möglich. |
Nichts ist unmöglich!
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| var i: Integer; begin for i := 1 to 20 do begin Inc(Integer(Pointer(@i)^)); end; |
Schön ist das nicht, führt zu nahzu unwartbaren Code, weil nicht auf dem ersten Blick erkennbar ist, was wirklich passiert, und überhaupt und sowieso. Vergesst das am Besten wieder ganz schnell, bloß NIE benutzen!! :?
jaenicke - Di 19.05.09 18:01
Ja, mit solchen Tricks kommt man auch teilweise an private Properties usw., und mit Assembler in die Schleife eingestreut geht das natürlich auch. :mrgreen:
BenBE - Di 19.05.09 18:37
Welche Programmiersprache brauch denn GOTOs??? Nur Come From ist das Wahre!
Und zu deinem Problem: Befehlsinterpreter sollten nicht mit einer FOR-Schleife arbeiten, sondern mit einem Instruction Pointer, der bei Sprüngen ggf. angepasst wird.
galagher - Di 19.05.09 18:53
Meine Lösung sieht nun Folgendes vor: Der unten stehende Code ist die Schleife, die die Datei zeilenweise abarbeitet, aber es gibt auch eine andere Prozedur mit Namen _goto, die die Integer-Variable g ändert. Leider komme ich auch im Delphi-Code nicht ohne
goto aus, denn irgendwie muss ich ja erreichen, dass die Schleife erneut mit dem neuen g-Wert beginnt!
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26:
| g := 0;
WCD:
for i := g to wcdList.Count-1 do begin if not (wcdList[i] = '') then begin try if not (wcdList[i][1] = ';') and not (GetWord(LowerCase(wcdList[i]), 1) = 'rem') and not (wcdList[i][1] = ':') then RxRichEdit1.Lines.Add(wcdList[i]);
if not (wcdList[i][1] = ':') then CommandExecute(wcdList[i]);
except ErrorText(sGeneralError); break; end;
if (GetWord(LowerCase(wcdList[i]), 1) = 'goto') then goto WCD; end; end; |
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| procedure _goto(sCmd, sSwitch, sParam: String; iCount: Byte); var i: Integer; begin for i := 0 to wcdList.Count-1 do begin if ':'+sParam = wcdList[i] then begin g := i; break; end; end; end; |
... Ja, und richtig gemütlich wird's vermutlich, wenn ich in meinem Programm beim
if angelangt bin!
jaenicke - Di 19.05.09 20:10
galagher hat folgendes geschrieben : |
| Leider komme ich auch im Delphi-Code nicht ohne goto aus, denn irgendwie muss ich ja erreichen, dass die Schleife erneut mit dem neuen g-Wert beginnt! |
Was hast du denn gegen das bereits angesprochene while? :lol:
Eine for-Schleife ist explizit dafür gedacht möglichst effizient den Wertebereich einmal in einer Richtung durchzugehen. Für alles andere ist diese spezielle Schleife nicht geeignet, dafür gibt es while oder repeat.
BenBE - Mi 20.05.09 09:00
Darf ich Schadensersatz für diese Missbräuchliche Nutzung von goto verlangen???
Um das mal dem Fragesteller zu zeigen, was gemeint ist:
Delphi-Quelltext
1: 2: 3: 4: 5:
| While not EndOfProgram do Begin Cmd := GetCmdAt(InstrPtr); ProcessCommand(Cmd); end; |
That's it ;-)
galagher - Mi 20.05.09 19:59
Ok, danke euch! Mal sehen, wie ich das goto im Delphi-Code wegbekomme!
ffgorcky - Mi 20.05.09 20:59
Also ich denke, wenn Dein Interpreter GOTO liest, dann müsste er doch nur in der Datei (also Deinem Memo, oder wie auch immer Du das zur Bearbeitung lädst) nach einem Punkt - also einer Zeile - suchen, der auf jeden Fall mit Doppelpunkt anfängt und das Wort beinhaltet, welches hinter goto steht und dort dann weiter arbeitet.
Aber einfacher wäre es, wenn Du das GoTo wirklich ganz weglassen könntest - was aber leider nicht immer so wirklich einfach ist.
Yogu - Mi 20.05.09 21:02
BenBE hat folgendes geschrieben : |
| Welche Programmiersprache brauch denn GOTOs??? Nur Come From ist das Wahre! |
Das ist einfach nur genial :rofl: Bau ich sofort in meine Scriptsprache ein!
Die ist übrigens auch gar kein so schlechter Vergleich, schau dir mal den Interpreter
dieses Tutorials [
http://wiki.delphigl.com/index.php/Tutorial_Scriptsprachen_Teil_2#Ein_Emulator_f.C3.BCr_unseren_Prozessor] an. Das Grundprinzip ist dort auch:
Register P zeigt auf den Befehl, der als
nächstes interpretiert werden soll. Der Befehl GOTO (dort heißt er
JMP) verändert einfach diesen Wert. Um zu wissen, wo sich denn welches Label versteckt, solltest du ganz am Anfang alle Labels in einer Liste bzw. in einem Array sammeln. Dann kannst du ganz einfach diese Liste durchsuchen. Das wäre sicherlich performanter als immer die komplette Datei abzuklappern.
galagher - Do 21.05.09 08:44
BenBE hat folgendes geschrieben : |
| Befehlsinterpreter sollten nicht mit einer FOR-Schleife arbeiten, sondern mit einem Instruction Pointer, der bei Sprüngen ggf. angepasst wird. |
:?: Wie sieht so etwas denn aus? Ein Beispiel?
Yogu hat folgendes geschrieben : |
| Um zu wissen, wo sich denn welches Label versteckt, solltest du ganz am Anfang alle Labels in einer Liste bzw. in einem Array sammeln. Dann kannst du ganz einfach diese Liste durchsuchen. Das wäre sicherlich performanter als immer die komplette Datei abzuklappern. |
Mal im Einzelnen: Die Datei ist in einer TStringList, da so zeilenweises Abarbeiten einfach ist - bis auf goto. Dann muss nämlich der Zähler geändert werden, und das erreicht man (zugegebenermassen unelegant), indem man die Liste ab jener Zeile, die das Label enthält, erneut abarbeitet.
Also: es kommt ein goto, das auf ein Sprungziel in der 10. Zeile verweist. Verlasse die Liste und lies sie erneut, diesmal aber nicht ab Zeile 0, sondern ab 9.
Delphi-Quelltext
1:
| for i := g to List.Count-1 do |
Ok, man kann das "Delphi-Code-goto" vermeiden, indem man den Code umstrukturiert, das kriege ich sicher hin. Aber mich interessiert, wie man das mit dem Instruction Pointer bewerkstelligt.
jaenicke - Do 21.05.09 08:48
galagher hat folgendes geschrieben : |
| :?: Wie sieht so etwas denn aus? Ein Beispiel? |
Hat er doch schon geschrieben. ;-)
Du nimmst eine Integervariable, die dir die aktuelle Zeile angibt und setzt die auf 0. Danach gehst du in einer while-Schleife die Befehle durch und erhöhst die aktuelle Zeile immer um 1. Und bei goto setzt du den Wert auf die entsprechende Zeile, und es geht dort weiter.
Die while-Schleife lässt du laufen, solange die aktuelle Zeile kleiner als die Anzahl der Zeilen ist. Ist die letzte zeile abgearbeitet springt der Zähler eins weiter und ist nicht mehr kleiner, dann bricht die Schleife ab.
BenBE - Do 21.05.09 12:33
jaenicke hat folgendes geschrieben : |
galagher hat folgendes geschrieben : | | :?: Wie sieht so etwas denn aus? Ein Beispiel? | Hat er doch schon geschrieben. ;-) |
Wenigstens einer liest meine Posts ...
:flehan: :flehan: :flehan:
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!