State Machine Compiler für 8-bit AVRs?

Sep 21, 2019 29 Replies

Hi Johannes,

Am 22.09.19 um 17:23 schrieb Johannes Bauer:

Ich bin bisher mit Ragel wirklich voll und ganz zufrieden und kann bisher alles damit abbilden was ich möchte. Wollte eigentlich nur wissen, was andere so verwenden. Dass das in so eine "angeregte Diskussion" ausufert, hätte ich nicht gedacht.

Danke, Gruß Andy

Ich warte ja auf das Gegenbeispiel, also: deinen C-Parser.

*Das* ist Geschwätz. Selbstverständlich hat C++ eine Grammatik. Zu finden im Standard-Dokument unter "Annex A" sowie in Fragmenten in den einzelnen Kapiteln. Sie ist halt nur nicht LL(1).

Nein. Sage ich als einer, der routinemäßig Parser per Hand baut.

(Das ist übrigens parserseitig völlig trivial. Für Pascal musst du nicht wissen, was das Wort 'x' bedeutet, um aus 'x(y)' einen AST bauen zu können.)

Das, was du beim Schreiben der Grammatik sparst - wenn überhaupt was - zahlst du beim Debuggen drauf. Wo muss ich jetzt die Aktionen platzieren? Wie bekomme ich das Speicherleck-frei? Wie bekomme ich gescheite Fehlermeldungen außer "parse error"? Wie erhole ich mich am besten von einem Parser-Fehler? (Nächstes Semikolon suchen? Oder doch lieber geschweifte Klammern? Oder 'END'-Schlüsselwort?)

Und - und das ist einer der Gründe, einen C-Parser per Hand zu machen - in einem händischen Parser ist es trivial, Feedback zu geben, was für ein Ding 'x' in 'x*y[3]' ist, denn abhängig davon wird der Rest geparsed. Mit einem generierten Parser muss man damit durch ein paar Ebenen Generat (yacc, lex) durch und dabei Annahmen treffen, wie der generierte Code sich benimmt.

Stefan

Nur weil einen Parser von Hand schreiben schwer ist, heißt das noch lange nicht, dass einen Parser mit Generator schreiben leicht ist.

Aber wenn ich ein Beispiel liefern würde, dann kann ich dir immerhin versichern, dass das nicht so grütze-mäßig Segfaulten würde wie DoDis Beispiel. Denn DAS ist Pfusch, völlig egal wie man parst.

Du kannst offenbar keine Standards lesen. Denn da steht ganz klar zuerstmal: Annex A (informative) -- muss ich dir jetzt den Unterschied zwischen informative und normative erklären?

Und dann nochmal überdeutlich: "This summary of C ++ syntax is intended to be an aid to comprehension. It is not an exact statement of the language."

Genau, verteilt über ~1300 Seiten, aber eben nicht ausdefiniert (und genau das habe ich geschrieben, keine ausdefinierte Grammatik).

Hat auch nie irgendjemand behauptet, völliges Strohmann-Argument.

Puh, naja, das ist jetzt Aussage gegen Aussage. Du findest das Schreiben von Hand einfach, ich habe gerne Unterstützung von Werkzeugen.

Das ist wohl richtig, eine ordentliche Grammatik zu schreiben ist auch alles andere als leicht. Und tatsächlich ist Debugging von Grammatiken elendige Arbeit.

Einen Tod muss man sterben. Vermutlich ist dann wohl ausschlaggebend, womit man am meisten Erfahrung hat.

Viele Grüße, Johannes

Da wäre ich mir nicht so sicher, denn Ressourcenmanagement zumindest mit yacc ist nicht einfach. Wer gackert muss auch legen.

(Mein zweites größeres Parserprojekt war vor >20 Jahren bei "Jugend Forscht", ist also nicht so, dass ich nicht legen könnte.)

Danke, sehr freundlich. (Mein ca. fünftes größeres Parserprojekt war Teil eines C++-Compilers für das VFiasco-Projekt. C++-Standard kenn ich.)

Das ist die Zusammenfassung der Fragmente. Nicht normativ, weil die normativen Teile nebst der semantischen Bedingungen in den anderen Kapiteln stehen, und weil dir niemand zusichert, in dem Anhang nichts vergessen zu haben.

Was wäre denn eine "ausdefinierte Grammatik"?

Die ernsthafte Schwäche, die diese Grammatik hat, ist, dass sie zwischen den Ebenen "lexikalische Analyse", "Präprozessor" und "syntaktische Analyse" nicht sauber trennt. Jedem Praktiker ist das egal. Regeln wie "maximum munch" (also dass 'inti' ein Wort ist und nicht das gleiche wie 'int i') werden quasi immer in Prosa angegeben, nicht in der Grammatik.

Stefan

Du vergisst die Messlatte: DoDi's Programm stürzt schon ab, wenn man es lediglich STARTET.

Ich bin mir extrem sicher, dass ich ein Programm hinkriege, dass nicht beim Start abstürzt. Eines, das nicht "F:\DoDi\..." als hardgecodete Pfade enthält.

Und einen Parser kann man auch in einer Programmiersprache schreiben, die Ressourcenmanagement übernimmt, ich bevorzuge Python wenn Performance nicht kritisch ist.

Joa und ich habe produktiven, sicherheitskritsichen Kernelcode geschrieben, der jahrelang unterbrechungsfrei laufen muss. Da kann man sich keine Ressourcenleaks erlauben, nicht mal die Kleinsten.

Na du musst dich schon entscheiden. Entweder du unterstellst mir Geschwätz, kannst das ordentlich belegen und ich halte meine Schnauze. Oder du unterstellst mir Geschwätz, lieferst unzureichende Belege und bekommst eine entsprechend unfreundliche Antwort. Beides geht nicht.

Mag ja sein, trotzdem hast du einen INFORMATIVEN Teil als "Beleg" angeführt. Einen Teil, der also als Beleg völlig untauglich ist, weil er formal eben NICHT Teil der Spezifikation ist. Und selbst wenn er es wäre ganz klar drin steht, dass es eben nicht den Sprachumfang abdeckt.

Und selbst die Fragmente sind horrend unvollständig. Ich nehme mir ein beliebiges Beispiel raus: "14.2. Names of template specializations". Eine popelige Grammatik, evtl 15 Zeilen lang. Gefolgt von 1 1/2 Seiten Prosa, die dann genau sagt wie die zu interpretieren ist.

Das ist keine formale Beschreibung einer Sprache. Das ist Prosa.

Eine formale Beschreibung, die man im idealfall direkt einem Parser vorlegen kann, also maschinenlesbar ist. C++ ist aber als Sprache so kompliziert, dass ich vermute, dass das wohl nicht (mehr?) machbar ist. Deswegen nutzt man eben viel Prosa und Beispiele, um zu verdeutlichen, was gemeint ist.

Ich würde sagen, die ernsthafteste Schwäche sind die extrem komplizierten Disambiguation Regeln. Die wirklich korrekt zu implementieren ist (auch aufgrund der mangelhaften Definition als textuelle Repräsentation) extrem schwierig und das zeigt sich auch immer wieder bei Beispielen, die von einem Compiler gefressen werden und von einem anderen nicht.

C++ ist halt Stückelwerk. Außerdem sind ihnen offenbar irgendwann die Tokens ausgegangen, sonst hätten wir nicht jahrelang elendige Probleme gehabt mir trivialsten Mist wie

std::vector<std::vector<std::string>>

bzw eben der früher notwendigen Variante

std::vector<std::vector<std::string> >

Wenn mich meiner Errinerung nicht täuscht, sind die ntowendigen Disambiguation Regeln dafür erst mit C++11 dazugekommen. Das ist einfach nur gruselig.

Gruß, Johannes

Am 27.09.2019 um 10:37 schrieb Johannes Bauer:

Das nennt sich - ich wiederhole mich - semantische Bedingungen. Von Trivialitäten wie Brainfuck abgesehen kommt kaum eine Sprache ohne aus.

Du kannst die Grammatik einem ausreichend fähigen Parsergenerator vorlegen. Der wird dann halt *mehr* erkennen als nur gültige C++-Programme, da die semantischen Bedingungen die gültigen Programme

*einschränken* ("eine Variable muss vor der Verwendung deklariert sein" usw.).

Diese Disambiguierungsregeln hat es zum Teil von C geerbt. 'a * b;' ist ein expression-statement oder eine Deklaration.

Das hat nichts mit schwammiger Disambiguierung zu tun, das ist ein ganz normaler "maximum munch" Lexer, der, wenn man ihm keinen Tipp gibt, '>>' als ein Token parsed.

Solche Effekte gibt es in vielen Sprachen. Pascal:

a := sqrt(.5);

(parsed nicht, weil '(.' als '[' interpretiert wird.)

In C++ hat man halt eine Sonderbehandlung für diesen einen Fall eingebaut.

Stefan

Am 27.09.2019 um 17:42 schrieb Stefan Reuther:

Schon K&R hat >>, ==, ++ usw. als eigenständige Tokens definiert. Selbst für +++ wurde geregelt, welche Tokens das ergibt. Und wenn wir schon dabei sind, das "dangling else" wird auch weit einfacher in der semantischen Prüfung des Parsers aufgelöst als in der Grammatik.

Mich stört da mehr das "long long", das eine eigene Bedeutung erhalten hat, nicht aber "short short". Ist eigentlich heutzutage auch "long signed long" ein gültiger Typ, und wenn ja welcher?

Sofern man Digraphen als Option eingeschaltet hat. Heutige Compiler kennen möglicherweise garkeine Di-/Trigraphen mehr, oder ignorieren sie per Default. Delphi unterscheidet inzwischen sogar Kommentare in { } und (* *), die eigentlich gleich interpretiert werden müßten. So kann man ganze Codeblöcke mit Kommentaren ggf. nochmal mit der anderen Schreibweise auskommentieren.

DoDi

Am 27.09.2019 um 21:22 schrieb Hans-Peter Diettrich:

"long signed long" ist das gleiche wie "long long".

Dafür gibt es wenigstens einen Compiler, der "long short" für einen

24-Bit-Typen kennt (MPLAB C18). Was sollte denn "short short" sein? Bei den üblichen Größen mit "char" = 8 Bit, "short" = 16 Bit bliebe ja nur ein 12-Bit-Typ übrig. Hardwareunterstützung dafür dürfte dann doch eher selten in freier Natur anzutreffen sein. Einfach, weil sich der Programmierer von der Straße nichts ohne 8-Bit-Bytes mehr vorstellen kann.

Zumindest die Turbo-Pascal-Compiler hatten keine mir bekannte Möglichkeit, das auszuschalten.

Das wiederum hatte Turbo Pascal auch.

Stefan

Klar, aber die Komplexität der in Prosa geschriebenen semantischen Bedingungen übersteigt die der Grammatik eben bei Weitem. Der Effekt ist bei C++ eben wirklich ausgeprägt.

Umso mehr Bedingungen es gibt, die diese Programme einschränken, umso nutzloser ist die reine formale Grammatik. Weil dann kann ich prinzipiell für jede Sprache eine allgemeingültige Grammatik angeben:

PROGRAM := chr(0..255)*

Supereinfach, und korrekt dazu. Parst jedes gültige C++-Programm. Und halt noch mehr, als semantische Bedingung gebe ich an muss zusätzlich den C++-Standard erfüllen. Feddich ist der Lack.

Ist aber halt super nutzlos, so eine Grammatik.

C ist im Vergleich zu C++ absoluter Kinderkram. Die Komplexität von C++ würde ich, bauchgefühlsmäßig, als mindestens Faktor 10 höher einschätzen.

Schon klar. Aber es geht nicht darum, wieso der Parser das so parst, sondern was per Definition die *korrekte* Art des Parsens ist. Und, wenn mich meine Errinerung nicht täuscht, war eben

std::vector<std:vector<std::string>>

Formal gesehen vor C++x ein Syntaxfehler. Ich glaube mich daran zu errinern, dass sowohl der MS C++-Compiler als auch der icc trotzdem das geparst haben, was "gemeint" war, und nur der gcc sich daran verschluckte. Und meiner Errinerung nach war gcc eben formal "korrekt". Aber das ist alles jetzt schon ewig her, bin mir in den Details nicht mehr ganz sicher.

Das verdeutlicht aber genau das Problem, das ich anspreche: Jemand, der einen Compiler schreibt, versucht das "richtige" zu machen, weil die formale Definition eben unvollständig oder klobig ist. Da wackelt der Schwanz mit dem Hund.

Gruß, Johannes

Am 28.09.2019 um 11:58 schrieb Stefan Reuther:

Warum? Ich hätte auf "signed long" getippt.

Das hat dann aber nichts mit einem C Standard zu tun, sondern ist eine compilerspezifische Erweiterung.

Deshalb schrieb ich ja "heutige" Compiler :-]

DoDi

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required