Die Übersetzung in C-Code ist eine sehr gut etablierte Gewohnheit. Das ursprüngliche C mit Klassen (und die frühen C ++ - Implementierungen, die damals als Cfront bezeichnet wurden ) haben dies erfolgreich durchgeführt. Mehrere Implementierungen von Lisp oder Scheme machen das, zB Chicken Scheme , Scheme48 , Bigloo . Einige Leute übersetzten Prolog zu C . Und einige Mozart- Versionen (und es gab Versuche, Ocaml-Bytecode nach C zu kompilieren ). Das CAIA-System mit künstlicher Intelligenz von J.Pitrat wird ebenfalls gebootet und generiert den gesamten C-Code. Vala übersetzt auch in C für GTK-bezogenen Code. Queinnecs Buch Lisp In Small Pieces habe ein Kapitel über die Übersetzung nach C.
Eines der Probleme bei der Übersetzung nach C sind schwanzrekursive Aufrufe . Der C-Standard garantiert nicht, dass ein C-Compiler sie richtig übersetzt (in einen "Sprung mit Argumenten", dh ohne Aufrufstapel zu essen), auch wenn in einigen Fällen neuere Versionen von GCC (oder von Clang / LLVM) diese Optimierung durchführen .
Ein weiteres Problem ist die Speicherbereinigung . Einige Implementierungen verwenden nur den konservativen Boehm-Garbage-Collector (der C-freundlich ist ...). Wenn Sie Code für die Garbage-Collection verwenden möchten (wie dies bei mehreren Lisp-Implementierungen der Fall ist, z. B. SBCL), könnte dies ein Albtraum sein (Sie möchten dlclose
Posix verwenden).
Ein weiteres Thema sind erstklassige Fortsetzungen und call / cc . Aber clevere Tricks sind möglich (siehe Chicken Scheme). Der Zugriff auf den Call-Stack kann viele Tricks erfordern (siehe GNU-Backtrace usw.). Das orthogonale Fortbestehen von Fortsetzungen (dh von Stapeln oder Fäden) wäre in C schwierig.
Die Ausnahmebehandlung ist oft eine Sache, um kluge Aufrufe an longjmp usw. zu senden.
Möglicherweise möchten Sie (in Ihrem ausgegebenen C-Code) entsprechende #line
Anweisungen generieren . Dies ist langweilig und gdb
erfordert viel Arbeit (Sie möchten, dass z. B. einfacher zu debuggender Code erstellt wird).
Mein MELT lispy domänenspezifische Sprache (anpassen oder erweitern GCC ) auf C (tatsächlich zu schlecht C ++ jetzt) übersetzt. Es hat einen eigenen Müllsammler. (Möglicherweise interessiert Sie Qish oder Ravenbrook MPS ). Generations-GC ist in maschinengeneriertem C-Code tatsächlich einfacher als in handgeschriebenem C-Code (da Sie Ihren C-Code-Generator auf Ihre Write-Barrier- und GC-Maschinerie zuschneiden).
Ich kenne keine Sprachimplementierung, die sich in echten C ++ - Code übersetzen lässt, dh mit Hilfe einer "Garbage Collection" -Technik zur Kompilierung von C ++ - Code, der viele STL-Vorlagen verwendet und die RAII- Redewendung respektiert . (Bitte geben Sie an, ob Sie einen kennen).
Was heute lustig ist, ist, dass C-Compiler (auf aktuellen Linux-Desktops) möglicherweise schnell genug sind, um eine in C übersetzte interaktive Top-Level- Lese-Evaluierungs-Druck-Schleife zu implementieren : Sie werden bei jedem Benutzer C-Code (einige hundert Zeilen) ausgeben Interaktion, Sie werden fork
eine Zusammenstellung davon in ein gemeinsames Objekt, das Sie dann würden dlopen
. (MELT macht das alles fertig und es ist normalerweise schnell genug). All dies kann einige Zehntelsekunden dauern und für Endbenutzer akzeptabel sein.
Nach Möglichkeit würde ich empfehlen, nach C zu übersetzen, nicht nach C ++, insbesondere weil die C ++ - Kompilierung langsam ist.
Wenn Sie Ihre Sprache implementieren, können Sie auch einige JIT- Bibliotheken wie libjit , GNU Lightning , asmjit oder sogar LLVM oder GCCJIT in Betracht ziehen (anstatt C-Code auszugeben) . Wenn Sie C übersetzen möchten, können Sie manchmal verwenden tinycc : es ist sehr schnell den generierten C - Code (auch im Speicher) kompiliert langsam Maschinencode. Im Allgemeinen möchten Sie jedoch die Optimierungen nutzen , die ein echter C-Compiler wie GCC vornimmt
Wenn Sie Ihre Sprache in C übersetzen, müssen Sie zunächst den gesamten AST des generierten C-Codes im Speicher erstellen (dies erleichtert auch das Generieren aller Deklarationen, dann aller Definitionen und des Funktionscodes). Auf diese Weise können Sie einige Optimierungen / Normalisierungen vornehmen. Sie könnten auch an mehreren GCC-Erweiterungen interessiert sein (z. B. computed gotos). Sie sollten es wahrscheinlich vermeiden, große C - Funktionen zu generieren - z. B. aus einer hunderttausenden Zeile generierten C - (Sie sollten sie besser in kleinere Teile aufteilen), da optimierte C - Compiler mit sehr großen C - Funktionen (in der Praxis) sehr unzufrieden sind experimentell,gcc -O
Die Kompilierungszeit großer Funktionen ist proportional zum Quadrat der Funktionscodegröße. Begrenzen Sie daher die Größe Ihrer generierten C-Funktionen auf jeweils einige tausend Zeilen.
Beachten Sie, dass C & C ++ - Compiler sowohl für Clang (über LLVM ) als auch für GCC (über libgccjit ) eine Möglichkeit bieten, einige für diese Compiler geeignete interne Repräsentationen zu emittieren. und ist spezifisch für jeden Compiler.
Wenn Sie eine Sprache entwerfen, die in C übersetzt werden soll, möchten Sie wahrscheinlich mehrere Tricks (oder Konstrukte) haben, um eine Mischung aus C und Ihrer Sprache zu generieren. Mein DSL2011-Papier MELT : Eine in den GCC-Compiler eingebettete übersetzte domänenspezifische Sprache sollte Ihnen nützliche Hinweise geben.