Hallo! Ich arbeite mich zur Zeit in das Thema Compilerbau ein und habe mir zu diesem Zweck das Dragon Book (Compiler) von Alfred V. Aho, Monica S. Lam, Ravi Sethi und Jeffrey D. Ullman zugelegt. Dieses arbeite ich gerade durch und konstruiere zur Zeit eine komplexere kontextfreie Grammatik, die eine einfache Sprache erkennen soll. Das ist natürlich nur eine kleine Übung sein. Ich baue dabei auf eine einfache Grammatik auf, die im Rahmen dieses Exkurses vorgestellt wird. Bisher habe ich die Grammatik um einen Modulo-Operator, die logischen Operatoren "|", "^" und "&", die zusammengesetzten Zuweisungsoperatoren ("*=", "%=" usw.), Post- und Pre- Increment- und Decrement-Operatoren, eine for-Schleife, ein switch-case-Konstrukt, sowie um Funktionsaufrufe und die Befehle DUMP, REUTRN und EXIT erweitert. Leider bin ich mir an einigen Stellen, wie zum Beispiel bei der Konstruktion des switch-case-Konstrukts, nichts sicher ob dies so korrekt ist. Überhaupt keine Ahnung habe ich, wie und wo ich in der Grammatik das Typecasting, den Trinären-Operator "(bool) ? (bool) : (bool)", einen sizeof()-Operator, die Deklaration eingener Funktionen, sowie eine Fehlerbehandlung mit REPORT oder THROW einbauen muss. Da diese Grammatik auch die Deklaration von Feldern ermöglicht, wäre es natürlich als kleines Bonbon noch sehr interessant, wie man Operatoren auf Mengen, wie union und intersect noch mit reinnehmen könnte, oder ob man diese auch einfach unter Funktionsaufrufe steckt und deren Namen und Operatoranzahl in einer Tabelle nachschlagen lässt. So würde ich es mit allen nicht elementaren Funktionen der von der Grammatik akzeptierten Sprache machen. Das bedeutet sie kommen in der Grammatik einfach nicht vor. Wäre dies ein übliches vorgehen? Wie sollte man sonst den später entstehenden Compiler erweitern, ohne überall im Quellcode herumpfuschen zu müssen. Würde mich sehr freuen, wenn sich jemand finden würde, der sich mit diesem Thema auskennt und Lust hat mir etwas unter die Arme zu greifen. Habe schon in einem anderen Forum gefragt und wurde nett darauf hingewiesen, dass der dort anzutreffende Programmierer-Mainstream (tolles Wortkonstrukt) davon nur wenig bis keine Ahnung hat. Vielen Dank im Voraus. Liebe Grüße, Andreas
Ich habe mir am leichtesten getan, wenn ich Grammatiken gleich als LL(1) Grammatiken aufgebaut habe. So was expr -> expr + term | expr - term | term ist nämlich IMHO nicht zu implementieren und nur als theoretisches Konstrukt brauchbar. Du hast nichts in der Hand was deinen Parser leiten würde. Welche der 3 Alternativen ist denn die richtige? Deine Grammatik ist super, wenn es darum geht unter einen Satz die entsprechenden terminalen Symbole zu schreiben und sich dann von dort aus bottom-up bis zum obersten Symbol durchzutasten. Aber die Umkehrung, einen Parser zu schreiben, der sich vom obersten Symbol zu den terminalen Symbolen durchtasten muss und Übereinstimmung mit den im Satz vorliegenden Symbolen festzustellen - hmmmmm Eine implementierbare Grammatik für Expression sieht zb in EBNF so aus expr -> term { ( "+" term ) | ( "-" term ) } . term -> factor { ( "*" factor ) | ( "/" factor ) } . factor -> ["-"] ( identifier | number | "sqrt" "(" expr ")" ) . Mit deiner Grammatik könnte ich nicht viel anfangen, die in ein Programm umzusetzen ... ich wüsste nicht recht wie ich das machen soll, ohne ständig in Endlosrekursionen zu laufen. Muss allerdings auch dazu sagen, dass meine Compilerbauvorlesungen schon lange her sind.
:
Wiederhergestellt durch User
Guten Abend! Vielen Dank für deine Antwort. Das Umformen meiner Grammatik zu einer LL(1) oder zumindest einer rechtsseitigen ableitenden Grammatik ist ja möglich. Dazu kann man die Tabelle der First- und Follow-Mengen der einzelnen Nichtterminale aufstellen. Diese können auch dabei helfen eine Sprache, wie sie von mir konstruiert wurde korrekt zu parsen. Endlosschleifen können außerdem recht einfach durch die richtige Verwendung des EPSILON Terminals, des leeren Wortes, vermieden werden. In deinem Beispiel sehe ich leider noch kein Unterschied zu meiner Grammatik oder soll dass meine in EBNF sein? Gibt es eigentlich eine Möglichkeit zu beweisen, dass eine Grammatik das tut, was sie tun soll, nämlich eine bestimmte Sprache erkennt. Kann mich nicht an einen konkreten Beweis erinnern. Liebe Grüße, Andreas PS: Werde heute oder morgen kurz die Regeln zur Umformung erläutern und auf meine Grammatik anwenden. EDIT: Entschuldigt der Beitrag meines Vorredners wurde gelöscht.
Andreas Wilhelm schrieb: > EDIT: Entschuldigt der Beitrag meines Vorredners wurde gelöscht. Ich wollte ihn eigentlich zurückziehen, weil ich mir nicht sicher bin, ob ich dir überhaupt damit helfen kann. Der Punkt ist der: Deine Grammatik ist eine schöne Übung, aber wie gesagt, zur Verwendung in einem Parser IMHO so nicht zu gebrauchen. D.h. ich denke du machst dir hier zuviel Arbeit. Auf der anderen Seite hab ich schon lange nichts anderes als LL(1) Grammatiken mehr gebaut. D.h. ich bin mit allem anderen reichlich aus der Übung.
Karl heinz Buchegger schrieb: > expr -> expr + term > | expr - term > | term > > ist nämlich IMHO nicht zu implementieren und nur als theoretisches > Konstrukt brauchbar. Diese linksrekursive Ausdrucksweise ist aber bei den Parser-Generatoren wie Yacc allgemein verbreitet, um nicht zu sagen geradezu typisch. Solange es sich im den gleichen Präfix handelt (hier "expr") entsteht kein Problem, weil der Parser sich noch garnicht für eine bestimmte Alternative entscheiden muss. http://www.cs.man.ac.uk/~pjj/cs212/ho/node5.html#SECTION00052000000000000000 Beim recursive descent parser sieht das freilich etwas anders aus.
Andreas Wilhelm schrieb: > Gibt es eigentlich eine Möglichkeit zu beweisen, dass eine Grammatik das > tut, was sie tun soll, nämlich eine bestimmte Sprache erkennt. Kann mich > nicht an einen konkreten Beweis erinnern. Sowas gibt es durchaus, allerdings in der (Uni-)Praxis nur an mehr oder weniger kleinen Trivialbeispielen. Bei allen größeren Sachen (wie z.B. etwas was schon halbwegs wie eine Programmiersprache aussieht) hat man ruck-zuck das Problem, wie man denn nun definiert, was man überhaupt erkennen möchte. Wenn man das nicht über eine Grammatik machen kann (das will man ja letztlich beweisen) dann gehen einem da sehr schnell die Mittel aus. Für Sprachen wie a^n b^n etc. besteht die Grammatik aus zwei bis drei Regeln, da hat man das schnell bewiesen, d.h. man entwickelt eine Konstruktionsvorschrift, die jedem Wort dieser Form eine Ableitungsfolge zuordnet und eben nachweist, dass diese Ableitungsfolge genau jenes Wort konstruiert. Umgekehrt muss man natürlich auch noch beweisen, dass jede (endliche) Ableitungsfolge die zur Grammatik passt in einem Wort der entsprechenden Sprache resultiert. Aber beschreib mal alle gültigen C-Programme in einer Form, die sich nicht auf einer Grammatik abstützt. Das kannst Du nicht sinnvoll machen, d.h. man bekommt nichtmal ordentlich eine Behauptung hingeschrieben, die man dann beweisen kann. Zu Deiner Grammatik: Die ist unnötig kompliziert, weil Du viele der Regeln zusammenfassen könntest, indem Du mehr Arbeit auf den (regulären) Tokenizer abwälzt. So kannst Du z.B. die Relations-Regeln (warum hast Du die überhaupt in "equality" und "rel" unterteilt?) zu einer zusammenfassen, die in der Mitte einen RELOP enthält. Es ist dann Aufgabe des lexing-Schritts, ">=", "<=", "==" etc. als RELOP zu erkennen. Später im Syntaxbaum kannst Du dann an einem Attribut des RELOP erkennen, welche Relation an dieser Stelle gemeint war. Damit werden es nicht nur erheblich weniger Regeln, sondern es wird auch klarer ersichtlich, welche Präzedenz Du bei Deinen Operatoren hast. Viele Grüße, Simon
A. K. schrieb: > Diese linksrekursive Ausdrucksweise ist aber bei den Parser-Generatoren > wie Yacc allgemein verbreitet, um nicht zu sagen geradezu typisch. > Solange es sich im den gleichen Präfix handelt (hier "expr") entsteht > kein Problem, weil der Parser sich noch garnicht für eine bestimmte > Alternative entscheiden muss. [...] > Beim recursive descent parser sieht das freilich etwas anders aus. Nunja, es ist aber eine Standard-Technik, linksrekursive Grammatiken entsprechend umzuformen, dass die Linksrekursion aufgelöst wird. Das ist genau das was lex und yacc machen. Für einen recursive descent parser kann man das natürlich von Hand machhen. Ok, es lässt sich natürlich drüber streiten, ob das noch die "gleiche" Grammatik ist, das ist aber eher eine philosophische Frage: es ist sicher, dass die gleiche Sprache erkannt wird. Viele Grüße, Simon
Andreas Wilhelm schrieb: > Gibt es eigentlich eine Möglichkeit zu beweisen, dass eine Grammatik das > tut, was sie tun soll, nämlich eine bestimmte Sprache erkennt. Kann mich > nicht an einen konkreten Beweis erinnern. Für einen solchen Beweis benötigst du eine formale Sprachdefinition, also eine Grammatik. Es läuft folglich darauf hinaus, die Äquivalenz zweier Grammatiken zu beweisen.
Simon Budig schrieb: > Nunja, es ist aber eine Standard-Technik, linksrekursive Grammatiken > entsprechend umzuformen, dass die Linksrekursion aufgelöst wird. Das ist > genau das was lex und yacc machen. Nur gibt es keinen Grund, sich eigens zu verknoten um eine rekursive Form zu vermeiden, wenn das der Parser-Generator nicht nur selber aufdröseln kann, sondern diese Formulierung geradezu erzwingt. Bei Yacc/Bison wird die in EBNF mit { ... } dargestellte Wiederholungform eben gerade per links/rechtsrekursiver Formulierung dargestellt. Man also die KHB'sche Form erst wieder in die passende rekursive Darstellung überführen muss - und dabei noch drauf achten muss, dass die optisch naheliegende Rechtsrekursion hier die falsche wäre. Insofern hängt die sinnvolle Spezifikation also davon ab, mit welchem Werkzeug man arbeitet. Bei Yacc/Bison rekursiv, bei recursive descent nicht (auch wenn's sprachlich naheläge ;-).
Hallo zusammen! Erst einmal vielen Dank für die zahlreichen Antworten. Simon Budig schrieb: > Für Sprachen wie a^n b^n etc. besteht die Grammatik aus zwei bis drei > Regeln, da hat man das schnell bewiesen ... Das kenne ich tatsächlich auch aus Vorlesungen, wie Info II, Theoretische Informatik oder Diskrete Mathematik. Wirklich helfen tut soetwas leider nicht wirklich, wenn man mal eine wirklich umfangreiche Grammatik vor sich hat. Da hast du wohl Recht. Simon Budig schrieb: > Zu Deiner Grammatik: Die ist unnötig kompliziert, weil Du viele der > Regeln zusammenfassen könntest, ... So kannst Du z.B. die Relations- > Regeln ... zu einer zusammenfassen, die in der Mitte einen RELOP > enthält. Das werde ich tun. Vielen Dank für diesen Tipp. Ich werde die neue Grammatik dann in nächster Zeit hier präsentieren. Karl heinz Buchegger schrieb: > Eine implementierbare Grammatik für Expression sieht zb in EBNF so aus Die nächste Grammatik werde ich auch in EBNF, wie sie YACC und Co. verwenden, vorstellen. Interessieren würde mich nun noch, wie ich in der alten Grammatik Funktionen und die Definition eigener Funktionen unterbringe. Erfasse ich in meiner Grammatik einfach eine Tokenfolge "ID LBRACKET params RBRACKET" und schaue dann im Schritt des Lexers, wie es auch Simon Budig für die Vergleichsoperatoren vorgeschlagen hat, den Namen und die damit verbundene Parameteranzahl nach? Freue mich auf weitere Anregungen und Hilfestellungen. Liebe Grüße, Andreas
Andreas Wilhelm schrieb: > Interessieren würde mich nun noch, wie ich in der alten Grammatik > Funktionen und die Definition eigener Funktionen unterbringe. Erfasse > ich in meiner Grammatik einfach eine Tokenfolge "ID LBRACKET params > RBRACKET" und schaue dann im Schritt des Lexers, wie es auch Simon Budig > für die Vergleichsoperatoren vorgeschlagen hat, den Namen und die damit > verbundene Parameteranzahl nach? Ja. Sobald der Benutzer selber Namen wählen kann geht das normalerweise über die reine Grammatik hinaus. Dann legst Du nur noch die syntaktischen Regeln für die Identifier fest (also z.B. welche Buchstaben man dafür verwenden darf). In dem Syntaxbaum, den der Parser aufbaut, werden dann die Lexeme (also das, was durch den Lexer in ein Token für den Parser gewandelt wurde) mitgespeichert. Dadurch kann man dann beim Auswerten des Syntaxbaums bei einem Funktionsaufruf feststellen, wie die aufgerufene Funktion denn hieß und dann entsprechend in einer Symboltabelle prüfen, ob diese Funktion existiert und sie ggf. ausführen. (An dieser Stelle verlässt die Korrektheit eines Programmtexts übrigens die Ebene der "Kontextfreiheit" - nur weil der Parser das Programm korrekt ableiten kann muss es nicht korrekt sein, z.B. weil eine Funktion aufgerufen wird, die nicht definiert wurde.) Viele Grüße, Simon
Andreas Wilhelm schrieb: > Interessieren würde mich nun noch, wie ich in der alten Grammatik > Funktionen und die Definition eigener Funktionen unterbringe. Erfasse > ich in meiner Grammatik einfach eine Tokenfolge "ID LBRACKET params > RBRACKET" und schaue dann im Schritt des Lexers, wie es auch Simon Budig > für die Vergleichsoperatoren vorgeschlagen hat, den Namen und die damit > verbundene Parameteranzahl nach? Ich habe Simon so verstanden, dass er den Lexer dazu bringen will, ein einziges Token für alle Vergleichsoperatoren gleicher Priorität abzuliefern, dessen Wert der exakte Operator ist, um die Grammatik zu vereinfachen. Das ist sinnvoll. Bei einem Symbol kann es zwar durchaus sinnvoll sein, wenn der Lexer bereits einen Verweis auf die Symboltabelle liefert, sofern das nicht in Konflikt mit disjunkten Namespaces gerät, aber ein Parser wird üblicherweise die Parameter einer Funktion erst alle einsammeln und allenfalls abschliessend feststellen, ob die Anzahl aktueller Parameter mit der Deklaration zusammenpasst. Syntaktisch ist es also zulässig, mehr oder weniger Parameter anzugeben, als für die Funktion deklariert sind.
Hallo! Danke für die Antworten. A. K. schrieb: > Ich habe Simon so verstanden, dass er den Lexer dazu bringen will, ein > einziges Token für alle Vergleichsoperatoren gleicher Priorität > abzuliefern, dessen Wert der exakte Operator ist, um die Grammatik zu > vereinfachen. Das ist sinnvoll. Das habe ich auch so verstanden, die Frage war ja nur, ob man ein ähnliches Verfahren für Standard-Funktionen der Sprache verwenden kann. Allerdings hast du natürlich Recht mit deinem Einwand bezüglich eines auftretenden Fehlers bei der Überprüfung der Parameteranzahl. Ein solcher Fehler tritt im Parser und nicht im Lexer auf. Simon Budig schrieb: > Sobald der Benutzer selber Namen wählen kann geht das normalerweise > über die reine Grammatik hinaus. Wie gesagt geht es mir momentan nicht um durch den Benutzer definierte Fuktionen, sondern um Standard-Funktionen, die Teil der Sprache sind. Wobei sich in diesem Zusammenhang natürlich auch die Frage stellt, ob diese nicht zusammengefasst werden könnten. Der Aufruf müsste dann ja die folgende Form haben:
1 | call -> ID ( optparams ) |
2 | | factor |
3 | optparams -> params |
4 | | EPSILON |
5 | params -> params , bool |
6 | | bool |
Welche Form muss aber eine Funktionsdeklaration erfüllen?
1 | calldef -> ID ( optdefparams ) ASSIGN bool ; |
2 | | factor |
3 | optdefparams -> defparams |
4 | | EPSILON |
5 | defparams -> defparams , ID |
6 | | ID |
Wäre das so richtig und falls ja, wo sollte ich das einbauen? Simon Budig schrieb: > ... warum hast Du die überhaupt in "equality" und "rel" unterteilt? Hier noch ein kurzer Nachtrag. Die Operatoren in equality und rel haben eine unterschiedliche Priorität - daher diese Unterteilung. Bezüglich der Priorität hätte ich auch noch eine Frage. Ich habe mich an einer Tabelle orientiert, die die Priorisierung der Operatoren in C erklärt (http://de.wikibooks.org/wiki/C-Programmierung:_Liste_der_Operatoren_nach_Priorität). Leider ist mir nicht ganz klar, wieso Präfix- und Postfix-Inkrement die gleiche Prioriät besitzen. Das ist meiner Meinung nach nicht wirklich logisch. Liebe Grüße, Andreas
Andreas Wilhelm schrieb: > Wäre das so richtig Ja. > Leider ist mir nicht ganz klar, wieso Präfix- und Postfix-Inkrement die > gleiche Prioriät besitzen. Das ist meiner Meinung nach nicht wirklich > logisch. Wo ist das Problem?
Hallo! A. K. schrieb: > Wo ist das Problem? Das Frage ich mich auch gerade. Hatte mich etwas an der Tatsache gestört, dass einmal vor allen anderen Aktionen der Wert erhöht wird und einmal danach. A. K. schrieb: >> Wäre das so richtig > > Ja. Das ist doch schonmal gut,wobei mir gerade auffällt, dass es wie folgt richtiger wäre.
1 | calldef -> ID ( optdefparams ) block ; |
2 | | bool |
3 | optdefparams -> defparams |
4 | | EPSILON |
5 | defparams -> defparams , ID |
6 | | ID |
calldef sollte zur Zuweisung kein Gleichheitszeichen verwenden, sondern es sollte einfach ein Block folgen. Außerdem sollte calldef wenn überhaupt auf bool und nicht auf factor zeigen. Die Frage ist nun, wo man es in der Grammatik einfügen müsste. Macht es nicht Sinn, es einfach gleich an den Anfang einzubauen? In diesem Fall sollte allerdings die Alternative bool wegfallen. Nachfolgend ein kleines Beispiel:
1 | prgm -> calldefs |
2 | calldefs -> calldefs calldef |
3 | | calldef |
4 | calldef -> ID ( optdefparams ) block |
5 | optdefparams -> defparams |
6 | | EPSILON |
7 | defparams -> defparams , ID |
8 | | ID |
Auf diese Weise wäre alles funktionsbasiert und man könnte, ähnlich wie in C, einfach beim Parsen nach einer main-Funktion suchen, die dann ausgeführt wird. Sollte ich das so in meine Grammatik, die ich noch einmal umgestellt, verkürzt und korrigiert in den Anhang gestellt habe, stellt sich mir noch die Frage, wie und wo ich Include-Befehle und Bedingungen (a ? b : c) einbauen kann. Außerdem bleibt noch die Frage, ob mein Konstrukt für eine Switch-Case-Anweisung korrekt ist. Meiner Meinung nach müssten Includes in das obige Beispiel wie folgt eingepflegt werden.
1 | prgm -> includes calldefs |
2 | includes -> includes include |
3 | | EPSILON |
4 | include -> INCLUDE " STRING "; |
5 | calldefs -> calldefs calldef |
6 | | EPSILON |
7 | calldef -> ID ( optdefparams ) block |
8 | optdefparams -> defparams |
9 | | EPSILON |
10 | defparams -> defparams , ID |
11 | | ID |
Würde mich über weitere Rückmeldungen, Korrekturen und Anregungen Freuen. Vielen Dank und liebe Grüße, Andreas \EDIT: Konnte Datei leider nicht anhängen, daher hier im Klartext.
1 | G := (N, T, P, S) |
2 | |
3 | S:= prgm |
4 | |
5 | T := { |
6 | "{", |
7 | "}", |
8 | CONST, |
9 | EPSILON, |
10 | ID, |
11 | NUMBER, |
12 | "[", |
13 | "]", |
14 | BASIC, |
15 | "||", |
16 | "&&", |
17 | EQUOP, |
18 | RELOP, |
19 | ASSIGN, |
20 | ";", |
21 | IF, |
22 | "(", |
23 | ")", |
24 | ELSE, |
25 | WHILE, |
26 | DO, |
27 | SWITCH, |
28 | BREAK, |
29 | RETURN, |
30 | STRING, |
31 | DEFAULT, |
32 | CASE, |
33 | "|", |
34 | "^", |
35 | "&", |
36 | "<<", |
37 | ">>", |
38 | "+", |
39 | "-", |
40 | "*", |
41 | "/", |
42 | "%", |
43 | "~", |
44 | "!", |
45 | "^", |
46 | ",", |
47 | TRUE, |
48 | FALSE |
49 | } |
50 | |
51 | N := { |
52 | prgm, |
53 | block, |
54 | decls, |
55 | const, |
56 | decl, |
57 | type, |
58 | stmts, |
59 | stmt, |
60 | loc, |
61 | optexpr, |
62 | cases, |
63 | bool, |
64 | join, |
65 | bitor, |
66 | bitxor, |
67 | bitand, |
68 | equality, |
69 | rel, |
70 | shift, |
71 | expr, |
72 | term, |
73 | unary, |
74 | call, |
75 | optparams, |
76 | params, |
77 | factor |
78 | } |
79 | |
80 | P := { |
81 | prgm -> block |
82 | block -> { decls stmts } |
83 | decls -> decls const |
84 | | EPSILON |
85 | const -> CONST decl |
86 | | decl |
87 | decl -> type ID; |
88 | type -> type [NUMBER] |
89 | | BASIC |
90 | stmts -> stmts stmt |
91 | | EPSILON |
92 | stmt -> loc ASSIGN bool ; |
93 | | IF ( bool ) stmt |
94 | | IF ( bool ) stmt ELSE stmt |
95 | | WHILE ( bool ) stmt |
96 | | DO stmt WHILE ( bool ) ; |
97 | | FOR ( optexpr ; optexpr ; optexpr ) stmt |
98 | | SWITCH ( bool ) cases |
99 | | BREAK ; |
100 | | RETURN bool ; |
101 | | block |
102 | loc -> loc [ bool ] |
103 | | ID |
104 | optexpr -> EPSILON |
105 | | bool |
106 | cases -> cases DEFAULT stmt |
107 | | cases CASE bool : stmt |
108 | | CASE stmt |
109 | bool -> bool || join |
110 | | join |
111 | join -> join && bitor |
112 | | bitor |
113 | bitor -> bitor | bitxor |
114 | | bitxor |
115 | bitxor -> bitxor ^ bitand |
116 | | bitand |
117 | bitand -> bitand & equality |
118 | | equality |
119 | equality -> equality EQUOP rel |
120 | | rel |
121 | rel -> bitor RELOP shift |
122 | | shift |
123 | shift -> shift << expr |
124 | | shift >> expr |
125 | | expr |
126 | expr -> expr + term |
127 | | expr - term |
128 | | term |
129 | term -> term * unary |
130 | | term / unary |
131 | | term % unary |
132 | | unary |
133 | unary -> + unary |
134 | | - unary |
135 | | ! unary |
136 | | ~ unary |
137 | | ++ unary |
138 | | -- unary |
139 | | unary ++ |
140 | | unary -- |
141 | | ( BASIC ) unary |
142 | | SIZEOF BASIC unary |
143 | | SIZEOF ID unary |
144 | | call |
145 | call -> ID ( optparams ) |
146 | | factor |
147 | optparams -> params |
148 | | EPSILON |
149 | params -> params , bool |
150 | | bool |
151 | factor -> ( bool ) |
152 | | loc |
153 | | NUMBER |
154 | | STRING |
155 | | TRUE |
156 | | FALSE |
157 | } |
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.