eigene scriptsprache
-
Hi,
mal ne Frage: Was für Vorkenntnisse aus dem Bereich hast Du denn?
Im Idealfall solltest Du ne Grammatik dafür erstellen (z.B. in Backus-Naur-Form) und die von Deinem Parser prüfen lassen.
Einfacher sollte die Zeile für Zeile Methode sein. Aber es wär natürlich wesentlich effizienter wenn Du das Script vorher komplett vorverarbeitest.
Hat natürlich den Nachteil das Dein Interpreter um einiges komplizierter wird und Du dich mit Compilerbau beschäftigen solltest.Ciao
-
Dieser Thread wurde von Moderator/in HumeSikkins aus dem Forum C++ in das Forum Rund um die Programmierung verschoben.
Im Zweifelsfall bitte auch folgende Hinweise beachten:
C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?Dieses Posting wurde automatisch erzeugt.
-
also ich würde sagen dass die methode mit zeile für zeile bei simplen sachen ausreicht (so mache ich das in meiner scriptengine, QBasic-like, non-OOP). aber wenn du funktionen hast, oder sogar teile der objektorientierung, dann denke ich ist preprocessing die bessere lösung.
... oder du schaust dir einfach http://www.angelcode.com/angelscript/ an. c++-like, objektorientierung möglich, recht performat, recht schlank.
und zlib-license
-
[quote="schrengtoi"]Guten morgen leute.
schrengtoi schrieb:
Ich bin gerade dabei mir ne kleine scriptsprache zu basteln.
Aber technisch habe ich da noch ein paar Fragen.
Also wie ich variablen anspreche usw. dürfte weniger das problem sein, wenn ich sie mit startzeichen "markiere" (z.B. mit '$' wie in php)Das ist sicher keine schlechte Idee, wenn man sich das Leben erleichtern will.
schrengtoi schrieb:
aber wie könnte ich am besten funktionsaufrufe kennzeichnen? Da will ich nicht unbedingt so ein zeichen vor dem Aufruf haben?
Wenn die Funktionsaufrufe (wie bei den meisten Sprachen) ihre Parameter in Klammern bekommen, dann kannst Du es doch an der Folge Identifier + Klammer (wahlweise mit oder ohne Spaces, hängt von Deiner Sprache ab) erkennen. Dann müssen halt auch Funktionen ohne Parameter mit foo() aufgerufen werden, aber das ist auch üblich und daher keine Umgewöhnung für die Anwender.
Das ist normalerweise Eindeuting, weil sonst vor den runden Klammern in Ausdrücken entweder nix oder ein Operator steht, aber niemals direkt ein Identifier.
schrengtoi schrieb:
Aber diese 2 sachen sind noch eher unwichtig.
Das wichtigste was ich nicht ganz folgern kann ist, wie ich blöcke richtig realisieren kann. (also alles was zwischen {} steht).Ich weiss nicht genau was Du meinst. Eine Möglichkeit wäre, jeweils die Interpreter-Funktion für eine öffnende Klammer rekursiv aufzurufen und zurückzukehren, wenn eine schliessende Klammer kommt. Um innerhalb einer Funktion/Prozedur an die lokalen Variablen zu kommen musst Du dann den jeweils gültigen Kontext (der z.B. die lokalen Variablen enthält) natürlich als Parameter mit übergeben.
schrengtoi schrieb:
Und wie ist der interpreter performanter: Zeile für zeile oder soll er alles komplett durchlesen, strukturieren und dann ausführen?
Performanter ist er, je mehr Du vorher machst, bevor Du die Ausführung beginnst.
Ich habe z.B. einen Interpreter der quasi immer den Text parst (nicht Zeile für Zeile, sondern Befehl für Befehl, weil man bei mir auch mehrere in eine Zeile schreiben kann) und ausführt. Das hat lange gereicht, bis die ersten wirklich anfingen komplexe Sachen darin zu machen.
Jetzt hab ich eine neue Version geschrieben die erst in einem Compiler-Lauf den Source in Bytecode konvertiert und diesen dann in einer VM ausführt. Der Unterschied war in meinem Fall etwa Faktor 200 und es ist jetzt schneller als Perl, aber der alte war auch nicht wirklich optimiert geschrieben und es war schon ein Stück Arbeit da hinzukommen.
Allerdings ist das ungleich komplizierter, und ich würde nicht raten, sowas ohne die Hilfe eines Generators zu machen, wenn die Sprache nicht sehr einfach aufgebaut ist. Für mich hat sich ANTLR sehr bewährt, ein Tool das die Grammatiken von Lexer und Parser (und bei Bedarf sogar Treeparser) einliest und daraus wahlweise Java/C++/C#/Python-Code erzeugt den man dann in sein Programm einbindet. Das ist noch kein Interpreter oder eine VM, sondern hilft nur bei der Bytecode-Erzeugung oder der Interpretation eines Syntaxbaumes, aber es nimmt ein enormes Stück Arbeit ab.
Also erst entscheiden was Du brauchst. Wenn es eher nicht so extrem auf Performance ankommt und die Syntax simpel ist, würde ich ruhig direkt interpretieren. Willst Du von der Performance in die Regionen von Perl/Python oder gar Lua vorstossen, musst Du Dich aber schon sehr strecken und auf jeden Fall eine Zwischendarstellung (wie Bytecode) erzeugen die schneller abgeaarbeitet werden kann.
(Wenn das zu kompliziert war, nochmal nachfragen
)
Viel Spaß, mir hat es jedenfalls welchen gemacht.
-
schrengtoi schrieb:
aber wie könnte ich am besten funktionsaufrufe kennzeichnen? Da will ich nicht unbedingt so ein zeichen vor dem Aufruf haben?
bei der sprache Profan wird ein @ als funktionzeichen benutzt
zudem kann man !$% usw. für verschiedene datentypen nutzen.rapso->greets();
-
danke erstmal für die Antworten.
Bei der Funktion war es echt naheliegend mit den (). Auf die idee bin ich nach dem posten meiner Frage übrigens auch gekommen
schrengtoi schrieb:
schrengtoi schrieb:
Aber diese 2 sachen sind noch eher unwichtig.
Das wichtigste was ich nicht ganz folgern kann ist, wie ich blöcke richtig realisieren kann. (also alles was zwischen {} steht).Ich weiss nicht genau was Du meinst.....
ich meine ich will diesen parser praktisch selber schreiben. d.h. mein Problem liegt darin, dass ich diese Verschachtelungen richtig einlesen u. verwalten kann.
Mit den Variablen bin ich noch am überlegen ob ich nur globale zulasse, oder eben auch welche, die nur im jeweiligen Speicherbereich gültig sind.
Dazu müsste ich eben solche speicherbereiche erstellen....naja ich werde mich noch ein bischen dran rumspielen, bei Fragen nerv ich euch einfach wieder
-
Eurer Meinung nach wäre es performanter den Code in einen Baum einzu lesen und dann mit Hilfe von virtuellen Methoden ausführen oder wirklich Bytecode erzeugen.
Mit einem Baum meine ich sowas:
class Code { virtual void exec()=0; }; class Block:public Code { public: void exec(){ for_each(i in(code.rbegin(), code.rend())) // Pseudo code thread.push(i); } private: vector<Code*>code; }; class If:public Code { public: void exec(){ if( /* condition */ ) thread.push(true_code); else thread.push(false_code); } private: Code*true_code,*false_code; }; //... stack<Code*>thread; thread.push( /* start code */ ); while(!thread.empty()){ thread.top()->exec() thread.pop(); }
Mit Bytecode meine ich soetwas:
class Variable; enum InstCode{ I_Cmp, I_Je, I_Jne }; union Instruction{ InstCode code; Variable*var; unsigned pos; }; //... vector<Instruction>code; bool result_flag; uint pos; while(pos < code.size()){ switch(code[pos].code){ case I_Cmp: result_flag = *(code[pos+1].var) == *(code[pos+2].var); pos += 3; break; case I_Je: if(result_flag) pos = *(code[pos+1].pos); else pos += 2; break; case I_Jne: if(!result_flag) pos = *(code[pos+1].pos); else pos += 2; break; } }
Ich hatte mal die erste Version implementiert und sie war zwar schnell aber langsamer als ich erwartet hatte. Es würde mich interessieren ob noch etwas drin gewesen wäre, hätte ich die Bytecode Variante gewällt.
ich meine ich will diesen parser praktisch selber schreiben. d.h. mein Problem liegt darin, dass ich diese Verschachtelungen richtig einlesen u. verwalten kann.
Ist eigentlich ganz einfach mit ein paar rekursiven Funktionen:
void parse_instruction(istream&in) { if(isdigit(in.peek())) // Nummer literal else if(in.peek() == '"') // String Literal else if(isalpha(in.peek())){ string name; while(isalpha(in.peek())) name += in.get(); ignore_spaces(in); if(in.peek() == '('){ // Funktionsaufruf parse_parameter(); if(in.get() != ')') error(); }else // Variable }else error(); } void parse_statement(istream&in) { if(in.peek() == '{') parse_block(in); else{ parse_instruction(); ignore_spaces(in); if(in.get() != ';') error(); } } void parse_block(istream&in) { if(in.get() != '{') error(); ignore_spaces(in); while(in.peek() != '}'){ parse_statement(in); ignore_spaces(in); } if(in.get() != '}') error(); }
So ich glaub das dürfte reichen um die grundlegende Idee zu verstehen.
-
joo . vielen dank.
-
Ben04 schrieb:
Eurer Meinung nach wäre es performanter den Code in einen Baum einzu lesen und dann mit Hilfe von virtuellen Methoden ausführen oder wirklich Bytecode erzeugen.
Das ist schwer zu sagen, zur Zeit scheinen die Code-basierten Sprachen schneller zu sein als die Baum-basierten.
Bei der Beispiel-Implementation für den Baum würde ich eher keinen Code-Stack manipulieren, sondern direkt die verketteten Knoten über ihre Verkettung "ausführen" da die Manipulationen des Code-Stacks nur zusätzliche Kosten verursachen.
Der Bytecode ist ja eher keiner, sondern (auf 32-Bit-Systemen) ein 32-Bit-Code. Ich hab in meiner tatsächlichen Bytecode benutzt, sprich die Elemente sind nur char groß. Das ist aber natürlich Abwägungssache. Bei mir war der Bytecode schneller als ein vorher (als Zwischenschritt) erzeugter 32-Bit-Code, aber letzterer ist natürlich einfacher zu erzeugen, da die Operanden konstante Grösse haben. (Die Codeerzeugung ist gerade auch für Sprünge kompliziert, wenn sich je nach Sprungweite die Operandengrösse ändert, dadurch aber wieder schon aufgelöste Sprünge angepasst werden müssen, wodurch sich deren Grösse wieder ändern könnte...)
Es spielt zudem bei einer VM noch eine Rolle ob sie Stack-Basiert ist oder Register-Basiert. Stack-basierte sind unzweifelhaft einfacher zu implementieren, aber zur Zeit tendieren die Auguren dazu das die Registerbasierten schneller sind und die aktuellen Versuche in der Richtung sehen so aus als ob dies stimmt. Speziell Lua 5 ist schon sehr flott und basiert auf einer Register-VM (Lua 4 hatte noch eine langsamere Stack-VM).
Ben04 schrieb:
Ich hatte mal die erste Version implementiert und sie war zwar schnell aber langsamer als ich erwartet hatte. Es würde mich interessieren ob noch etwas drin gewesen wäre, hätte ich die Bytecode Variante gewällt.
Da das von den Instruktionen und den durchschnittlichen Programmen abhängt ob und wie viel es bringt, ist das schwer zu beantworten. Meine Bytecode-VM macht zur Zeit auf meinem 2GHz-Schleppi 30-35 Millionen Bytecode-Befehle pro Sekunde und ein Primzahlenbeispiel läuft mit einer Geschwindigkeit zwischen dem entsprechenden Code in Perl und Python. Die Sprache ist dynamisch typisiert, was natürlich langsamer ist, als eine statisch typisierte Skriptsprache, aber dynamische Typisierung ist ja in Scriptsprachen auch üblicher. Aber ich habe halt keine Baum-Variante von der gleichen Sprache gemacht, also kann auch ich nicht sagen wo man dann landet. Es hängt auch davon ab, ob man evtl. später auch mal den Zwischencode speichern will, das ist mit Bytecode schon einfacher und vermutlich kompakter als mit einem Baum.
-
Ben04 schrieb:
Eurer Meinung nach wäre es performanter den Code in einen Baum einzu lesen und dann mit Hilfe von virtuellen Methoden ausführen oder wirklich Bytecode erzeugen.
endlich mal einer, der die RICHTIGE FRAGE (TM) stellt.
sagen wir mal der baum ist rekursuv deviniert und heißt eigentlich Node.
also ein Node kann subNods besitzen.dein
struct Node{ void interpret();//name von der redaktion geändert }
ist schon gut.
und der rest ist pillepalle.
beachte zuerst mal
struct Node{ void interpret(); Node* optimoze(); }
ein optimierer auf hoch-ebenbe! ist das nicht geil!? der wird alle zur compilzeit brechenbaren ausdrücke berechnen, der wird NodePlus(NodeInt(5),NodeInt(7)) leicht zu NodeInt(12) machen können.
dann natürlich
struct Node{ void interpret(); Node* optimize(); string compileToBytecode(); }
da haste deinen bytecode.
aber das bytecode-compilieren soll nur triviales nebenprodukt sein. keine 32 gedanken daran verschwenden. einfach hopllahopp den generieren. der benutzer kann auf seinem zielsystem einfach messen, ob interpret() oder run(compile()) schneller ist. interpret() brauchste für ein gutes optimize(). oder bin ich jetzt wieder meinen träumen verhangen, wo alles gut ist und die sonne immer scheint?
also ich votiere klar für interpret();
-
Dazu vielleicht noch ein Nachtrag: Jupp, es macht auf jeden Fall Sinn einen Baum zu erzeugen und einen guten Teil der Optimierungen dort auszuführen, dazu gehören sicher constant Expressions, die freundlicherweise trivial sind, spannender sind da schon Common Subexpressions, aber natürlich nicht so einfach zu finden, da sie von den Variableninhalten abhängen und nicht jeder gleiche Teilbaum zusammengefasst werden kann. Will Man Bytecode erzeugen, so kann man dann auf der Seite noch heufig vorkommende Folgen in Komplexere Bytecodes zusammenfassen, dort finden sich auch Optimierungen die im Baum nicht oder viel schwieriger zu entdecken sind, da sie über Teilbaumgrenzen hinweg entstehen können.
Insgesammt kann man massig Arbeit und Zeit in den Optimizer stecken und ich kann nur empfehlen, durch Profiling zu prüfen was man wirklich braucht, insbesondere wenn man abschätzen kann das später ein bestimmter Typ von Problemlösungen damit gescriptet werden soll. Es mag sich dann lohnen speziell auf diesen Bedarf hin zu optimieren.
Ich bin übrigends schon neugierig, wofür die Sprache eingesetzt werden soll. Game-Logik (wie viele Eigenentwicklungen), Anwendungs-Erweiterung oder General Purpose?
-
ScriptMe schrieb:
Will Man Bytecode erzeugen, so kann man dann auf der Seite noch heufig vorkommende Folgen in Komplexere Bytecodes zusammenfassen, dort finden sich auch Optimierungen die im Baum nicht oder viel schwieriger zu entdecken sind, da sie über Teilbaumgrenzen hinweg entstehen können.
jup!
ohne frage kann man den assembler-code noch optimieren und kommt erst dann auf das optimale ergebnis. halt das verzwirbeln, um optimale registerbelegung zu bekommen und die ganze parallelität des prozessors auszunutzen.
je nach bytecode hat der selber auch optimierungsmöglichkeiten. je weiter er vom baum weg ist, desto mehr.
-
ScriptMe schrieb:
Das ist schwer zu sagen, zur Zeit scheinen die Code-basierten Sprachen schneller zu sein als die Baum-basierten.
Dann würde ich aber Äpfel mit Birnen vergleichen. Die Bytecodeversion kann in jeder Situation unterbrochen werden, die Baum-basierte nur wenn sie einen Stack hat. Dies ist sehr praktisch wenn zum Beispiel einen Debugger basteln will.
volkard schrieb:
beachte zuerst mal
struct Node{ void interpret(); Node* optimoze(); }
ein optimierer auf hoch-ebenbe! ist das nicht geil!? der wird alle zur compilzeit brechenbaren ausdrücke berechnen, der wird NodePlus(NodeInt(5),NodeInt(7)) leicht zu NodeInt(12) machen können.
Ich sehe du scheinst Erfahrung auf diesem Gebiet, also gleich noch eine Frage. Wenn ich die Hochsprachenoptimizirungen wirklich einbaue dann braucht ich ja mehr als ein optimize(). Das hier ist ja das Minimum:
struct Node{ Node*optimize(); bool is_constant(); Value reduce_to_constant(); bool has_side_effects(); bool has_sub_expression(const Node*); bool equals(const Node*); bool has_tail_recursion(); //... };
Je mehr Optimierungen man einführt, je mehr werden die eigentlichen Node Klassen, welche ja eigentlich nur die Aufgabe haben Code zu repräsentieren, aufgeblasen. Des weiteren sind die Methoden die an einer Aufgabe werkeln sehr schnell weit verteilt (oder man organisiert die cpp Dateien nicht nach Klassen). Hast du nicht eine Idee wie man hier die verschiedenen Optimierungsmethoden von einander und vor allem von den Nodes trennt damit die Übersicht erhalten bleibt? Alle meine bisherigen Ansätze diesbezüglich endeten mit Code der schon in den frühen Entwicklungsstadien unwartbarer wurde.
-
Ben04 schrieb:
Ich sehe du scheinst Erfahrung auf diesem Gebiet, also gleich noch eine Frage.
hab es noch nicht versucht. hab nur vorüberlegungen betrieben, auchmal sowas zu machen.
Alle meine bisherigen Ansätze diesbezüglich endeten mit Code der schon in den frühen Entwicklungsstadien unwartbarer wurde.
oh. du bist viel weiter als ich. ich hab mir nur vorgestellt, daß optimize im wesenlichen klappt. in sachen wartbarkeit könnte ich nur helfen, wenn ich schon programmierversuche gemacht hätte.
-
hi ich bins nochmal.
also hab mich da mal bissl reingesteigert und bin sogar schon garnicht so wenig weit gekommen. Im moment Arbeite ich noch am "Variablen" system. Wo gleich die nächste Frage aufkommt: ich hätte gerne, das der backslash "funktionier" so wie man das gewohnt ist.
Also sprich "\"das ist innem string\"" soll dann eben die \"\" zu "" machen wenn die äußeren aufgelöst werden. und auch alle anderen zeichen mit \ davor.
weil ich denke, dass es bessere methoden als zeilenlange switchanweisungen gibtdanke schonmal
-
schrengtoi schrieb:
Wo gleich die nächste Frage aufkommt: ich hätte gerne, das der backslash "funktionier" so wie man das gewohnt ist.
Also sprich "\"das ist innem string\"" soll dann eben die \"\" zu "" machen wenn die äußeren aufgelöst werden. und auch alle anderen zeichen mit \ davor.
weil ich denke, dass es bessere methoden als zeilenlange switchanweisungen gibtdanke schonmal
Dies sollte die Aufgabe des sog. Lexers sein, der aus einer Skript-Datei eine Folge von "Tokens" generiert. Ich habe das auch mal gemacht.
Kannst du dir ja mal anschauen, der Lexer ist in src/compiler/cllexer.cpp:
http://www.zak2project.de/files/tech/cl2-31-03-2005.zip. Außerdem werden C- und C++-Kommentare entfernt.
(Meine Skriptsprache selbst ist nur auf Einfachheit ausgelegt - keine Optimierung, aber Bytecode, ungefähr 5x so langsam wie Perl)-Gunnar
-
Ich hab auch mal eine kleine Skriptsprache implementiert, ist auch nicht optimiert sonder ganz geradeaus interpretiert. Geschwindigkeit keine Ahnung, aber vielleicht hilft's ja jemand
Implementierung ist allerdings in C#.
CScript
-
schrengtoi schrieb:
hi ich bins nochmal.
also hab mich da mal bissl reingesteigert und bin sogar schon garnicht so wenig weit gekommen. Im moment Arbeite ich noch am "Variablen" system. Wo gleich die nächste Frage aufkommt: ich hätte gerne, das der backslash "funktionier" so wie man das gewohnt ist.
Also sprich "\"das ist innem string\"" soll dann eben die \"\" zu "" machen wenn die äußeren aufgelöst werden. und auch alle anderen zeichen mit \ davor.
weil ich denke, dass es bessere methoden als zeilenlange switchanweisungen gibtdanke schonmal
An sich hat das aber eigentlich nicht sehr viel mit einem Variabelensystem zu tun. Ein Stringliteral lässt sich meiner Erfahrung nach am besten mit einer Inlinefunktion die den String als Rückgabewert hat implementieren.
Das Parsen ist trivial:
char match_escape_sequence(char c){ switch(c){ case '\'':case '\\':case '\"':return c; case 'n':return '\n'; default:error(); } } string read_string(istream&in) { ignore_spaces(in); if(in.peek() != '\"' && in.peek() != '\i') error(); char enclose = in.get(); string str; int c; while((c = in.get()) != enclose){ if(c == EOF) error(); else if(c == '\\'){ c = in.get(); if(c == EOF) error(); str += match_escape_sequence(c); }else str += c; } return str; }
Parserfunktionen schreiben gehört eigentlich sehr oft zu den Teilen eines Interpreter die am meisten Spaß machen zu schreiben.
Zum Thema Token habe ich die besten Erfahrungen mit folgendem Lexer gemacht:
bool is_at_string(istream&in); string read_string(istream&in); bool is_at_number(istream&in); double read_number(istream&in);
istream sollte man aber noch durch eine eigene Kreation ersetzen die ihre Position verhält damit man ordentlich Fehler melden kann und die auch richtig Seeken kann und nicht diese Arrayemulation die man mit istream::seekg betreibt, dadurch dass man einen Offset angibt. Solange dieser Offset da ist wird das nie performant und speichersparend.
Oft versuchen Leute eine große Lexerclasse zu schreiben und dieser dann eine Methode ReadToken zu verpassen. Dies hat ein großes Problem : man versucht viele grundverschiedene Tokentypen über einen gemeinsamen Returntype zu quetschen. Das wird nie schön und endet fast immer in einer Struktur wie:
struct Token{ string value; enum TokenType{ T_String, T_Number }type; };
Meiner Meinung nach erfüllt solch ein Lexer noch nicht mal seine Aufgabe. Ein Token "0xFF" ist meiner Meinung nach nicht verschieden von einem Token "255" dieser Lexer trägt dem aber keine Rechnung oder es wird sehr viel gecastet:
- "0xFF" und "255" => int
- int => string
- dursch ReadToken
- string => int
Dies ist aber meine Meinung und es gibt auch Leute die sagen ein Lexer sollte ausschließlich die einzeln Token von einander abgrenzen und das Lesen der Token außerhalb ihres Kontextes gehört nicht mehr zu seiner Aufgabe. Dies empfinde ich allerdings als ein unnötiger Mehraufwand.
read_string und read_number sollten auch meiner Meinung nach auch global sein. Sie in eine Klasse zu quetschen bringt keinen Gewinn schließlich sind es ja zustandlose Prozeduren. Ein eigener Namensraum wäre allerdings sicher nicht verkehrt.
oh. du bist viel weiter als ich. ich hab mir nur vorgestellt, daß optimize im wesenlichen klappt. in sachen wartbarkeit könnte ich nur helfen, wenn ich schon programmierversuche gemacht hätte.
Schade.
Dann muss ich wohl diesbezüglich noch weiter in meinem Labor forschen.
-
so. Ich grab nochmal den alten thread raus.
Bin wieder ein bischen weitergekommen .... jetzt steh' ich nurnoch vor einem riesen Problem.Wie realisiere ich am besten If-Abfragen? ich habs mir soweit überlegt:
ich laufe in einer schleife alle bedingungen, getrennt druch "&&" oder "||", durch.
Das mache ich in eine do-while schleife, damit er es auch einmal ausführt wenn keins von beiden existiert.
aber wie checke ich jetzt weiterhin, will er nur prüfen ob die variable existiert, bzw. ob sie true ist (mit if($la) z.B). Will ers nicht, kann ich nach einem ! suchen, aber wann sollte ich das am besten suchen?
und in welcher Reihenfolge soll ich das abarbeiten, auch zzgl. den anderen Operatoren "<=" ">=" "==" "!="?mfg schrengtoi
-
Suche das Trennzeichen mit der niedrigsten Priorität, spalte dort auf und suche wieder rekursiv das Trennzeichen mit niedrigster Priorität. Wenn es kein Trennzeichen im Teilbereich mehr gibt, werte den Ausdruck aus.
Du könntest auch Ook! als Skriptsprache verwenden.