Die Entwicklung von Softwarebibliotheken für verschiedene Programmiersprachen ist eine anspruchsvolle Aufgabe, die vielfach mit immensem Aufwand verbunden ist. Immer wieder stellt sich die Frage, wie komplexe Software, ursprünglich in einer Sprache wie C++ geschrieben, für andere Plattformen und Entwicklerwelten zugängig gemacht werden kann, ohne dabei auflaufenden Wartungsaufwand zu riskieren. Zahlreiche Entwickler sehen sich vor der Herausforderung, ihre Arbeit einem größeren Publikum zur Verfügung zu stellen und ihre Codebasis dennoch übersichtlich und wartbar zu halten. Ein bemerkenswerter Ansatz ist dabei die Kreation einer eigenen, maßgeschneiderten Programmiersprache. Am Beispiel von Number Duck, einer Softwarebibliothek für das Lesen und Schreiben von Excel-Dateien, lässt sich genau nachverfolgen, wie solch ein ehrgeiziges Projekt realisiert werden kann.
Number Duck begann ursprünglich als eine spezialisierte C++-Bibliothek, die sich auf den Umgang mit Excel-Dateien konzentrierte. Obwohl diese Bibliothek für fortgeschrittene Entwickler äußerst nützlich war, beschränkte die Bindung an C++ die Reichweite spürbar. In der Geschäftswelt dominieren Sprachen wie C# oder Java – besonders im Unternehmensumfeld sind diese Sprachen weit verbreitet und etablierter Standard. Das Bedürfnis, Number Duck auch für solche Umgebungen zugänglich zu machen, war der Auslöser für die Reise vom reinen C++-Projekt hin zu einer plattformübergreifenden Lösung ohne Abhängigkeiten von Laufzeitumgebungen oder Wrappern. Ein erster Versuch richtete sich darauf, die C++-Codebasis mithilfe von Clang, einem quelloffenen Compiler, zu analysieren.
Clang ermöglicht das Parsen von C++-Code in einen Abstrakten Syntaxbaum (AST), mit dessen Hilfe theoretisch eine Übersetzung in andere Sprachen erfolgen könnte. Durch die Analyse des AST wollte der Entwickler sowohl C++ als auch C#-Code automatisch erzeugen. Dies war ein vielversprechender Start, der dabei half, zentrale Probleme aufzudecken – insbesondere die feingliedrige Speicherverwaltung und direkte Speicherzugriffe, die in C++ vielfach verwendet werden, aber in memory-safe Sprachen wie C# nicht ohne weiteres möglich sind. Ein besonderes Problem stellte das alte Excel BIFF8 XLS-Dateiformat dar, das eine extrem präzise und komplexe binäre Datenmanipulation erforderte. Number Duck setzte dafür umfassend Pointerarithmetik ein, die direkt auf den Speicher zugreift.
In Sprachen wie C#, die auf Sicherheit und Schutzmechanismen gegen solche direkten Speicherzugriffe setzen, kann dieser Code nicht 1:1 übernommen werden. Um das zu lösen, erdachte der Entwickler eine Abstraktionsschicht für Speicheroperationen. Diese Schicht stellte eine Art gemeinsamen Nenner für C++ und C# bereit, in dem Speicherzugriffe kontrolliert, abstrahiert und sicher ausgeführt wurden. Mit Hilfe dieser Abstraktion musste der bestehende C++-Code umgeschrieben werden, so dass alle direkten Speicherzugriffe über diese Schicht liefen. Das war ein zeitaufwendiger Prozess, zahlreicher Code musste angepasst werden, doch diese Grundlage ermöglichte es später, den gleichen Code plattformübergreifend zu verwenden und sicher auszuführen.
Neben der Speicherverwaltung mussten auch andere Fremdbibliotheken, etwa zur Verarbeitung von XML oder ZIP-Dateiformaten, abstrahiert werden, da hier in den Zielumgebungen unterschiedliche Bibliotheken zum Einsatz kommen. Indem eine einheitliche Schnittstelle geschaffen wurde, konnten diese Unterschiede überbrückt werden. Trotz des Fortschritts stieß der Entwickler bald an Grenzen. Die Nutzung von Clang AST zum Übersetzen und Generieren von C#-Code erwies sich als zu umständlich und begrenzte die Freiheit, neue Sprachkonstrukte einzuführen. Besonders die Einführung von Konzepten wie "besessenen" Zeigern (owned pointers) war in Standard-C++ kaum möglich.
Die Idee, eine eigene Sprache zu entwickeln, wurde geboren – eine Sprache, die ähnlich wie eine reduzierte Version von C# aussehen sollte, aber speziell auf die Bedürfnisse von Number Duck und die problematischen Speicherbereiche abgestimmt war. Die Suche nach existierenden Transpilationslösungen brachte zwar einige Ergebnisse, doch jede hatte Kompromisse, die für das Projekt nicht akzeptabel waren – sei es wegen unerwünschter Laufzeitabhängigkeiten oder weil die Zielsprachen nicht passend unterstützt wurden. Aus diesem Grund entschied sich der Entwickler, mit den Grundlagen aus dem Buch "Crafting Interpreters" eine eigene, simple Sprache zu erschaffen. Diese neue Sprache sollte klare, begrenzte Sprachmerkmale haben, und vor allem intelligente Speicherverwaltung unterstützen. Der erste Transpiler, der die neue Sprache in C++ und C# übersetzte, wurde in C++ geschrieben.
So konnten erste Tests mit einfachen Beispielen gemacht werden, um das Konzept zu validieren. Schon bald wurde der Transpiler jedoch in die neu entwickelte eigene Sprache portiert und damit selbstgehostet. Der enorme Vorteil: Der Transpiler wurde zu einem echten Praxistest für die Sprache selbst, Verbesserungen konnten schnell implementiert werden und die Sprache wurde gleichzeitig um relevante Funktionen erweitert. Die Arbeit mit eigenem Stable- und Unstable-Code ermöglichte eine kontrollierte Weiterentwicklung und sorgte dafür, dass Fehler schnell behoben werden konnten, ohne die Gesamtentwicklung zu gefährden. Hat man einmal eine stabile eigene Sprache und einen funktionierenden Transpiler aufgebaut, konnte die eigentliche Portierung von Number Duck beginnen.
Anstatt alles auf einmal zu machen, wurde eine schrittweise, file-by-file Methode gewählt. Jedes einzelne Quelltext-Dokument wurde in die neue Sprache übertragen, dann mittels Transpiler wieder in C++ konvertiert und ersetzt. Zwischendurch wurde die komplette Bibliothek gebaut und getestet, um sicherzugehen, dass alle Funktionen wie bisher arbeiteten. Auf diese Weise blieb der Code stets funktionsfähig und Fehler konnten viel schneller lokalisiert und korrigiert werden – eine Strategie, die Geld, Zeit und Nerven spart. Besonders wichtig war ein umfangreiches Test-Framework für Number Duck.
Dieses stellte sicher, dass die transpilierte Version der Bibliothek die gleichen Ergebnisse lieferte wie der Originalcode. Nur so war gewährleistet, dass an keiner Stelle Funktionsverluste oder unerwünschte Fehler auftreten konnten. Nachdem die gesamte Codebasis erfolgreich migriert war, widmete sich der Entwickler weiteren Optimierungen. Die vielen Einzelfiles wurden zu einem Amalgam zusammengeführt, um die Handhabung zu erleichtern. Zusätzlich wurden alle plattformübergreifenden Codeschnipsel integriert – von Speicherabstraktionen bis hin zu Drittanbieterbibliotheken wie TinyXML2 im C++-Bereich.
Das Ergebnis ist kein perfektes Kunstwerk, aber ein praktisch anwendbares und funktionierendes System. Der Prozess zeigt eindrucksvoll, dass technologische Herausforderungen oft weniger in der technischen Machbarkeit als im Durchhaltevermögen und strukturierten Vorgehen liegen. Mit den entsprechenden Sicherheitsmechanismen wie automatisierten Tests und stabilen Builds lässt sich scheinbar Unmögliches bewältigen. Der Wechsel weg von der traditionellen C++-Codebasis hin zur selbst entworfenen Sprache eröffnete zudem eine klare Perspektive: Neue Features, Erweiterungen oder Anpassungen müssen künftig nur einmal umgesetzt werden und sind automatisch in allen Sprachen nutzbar. Dies reduziert den Pflegeaufwand drastisch und erhöht die Konsistenz.
Die Erfahrung zeigt zudem, dass ambitionierte Projekte dieser Art zwangsweise ressourcenintensiv sind und Zeit benötigen. In der Praxis bedeutet dies, dass während der Portierung meist keine neuen Features entwickelt werden können – eine Investition in die Zukunft. Doch die Konnektivität und Zugriffsmöglichkeiten über mehrere Programmiersprachen kompensieren diesen Aufwand mehr als deutlich. Der Weg, eine eigene Programmiersprache zu entwickeln und als Transpiler für eine bestehende Bibliothek zu verwenden, ist sicher kein Standardweg. Es bedarf viel Mut, Know-how und Geduld.