Ich halte ein Projekt, einen Compiler für eine Python-artige Sprache für durchaus machbar. Tatsächlich finde ich auch die Grundlagen des Compilerbaus: Parsing und Code Generation vergleichsweise einfach. Ich würde mir jetzt nicht geben wollen, ein komplexes Typsystem zu entwerfen, vielleicht sogar noch zu beweisen, dass es "sound" ist, oder eine optimierer für 3-Address-Ausdrücke und Codeblöcke zu entfernen, aber wenn man C als Zielsprache nimmt, macht dieser Compiler das "heavy lifting".
Hier habe ich einen Python-Interpreter (ohne Laufzeitsystem) in Python geschrieben:
https://gist.github.com/760937
Unter Namensnennung darfst du dich da gerne am Code bedienen. Das AST-Modul ist natürlich ein weiterer Weg, wie man sehr schnell zu einem AST kommt, aus dem man dann Code generieren kann. Dann muss man aber in jedem Fall kompatibel zur Syntax von Python 2 oder 3 bleiben.
Ich fand es übrigens nicht notwendig, einen Parser-Generator zu benutzen, denn Python ist sehr simpel strukturiert und die einzige Schwierigkeit ist der Lexer, der Einrückung in Token umwandeln sollte, damit der Parser einfacher ist. Auch wenn ich's nicht verstehe gibt es AFAIK keinen fertiger Generator, der Sprachen mit Einrückung beherrscht, hier müsste man also eh den Lexer selbst schreiben. Und der Rest ist dann Routine.
Das ich Standard-C und nicht Assembler (oder gar Maschinencode) erzeugen würde, schrieb ich ja schon.
Eine wichtige Entscheidung ist IMHO, ob du eine automatisch Speicherverwaltung haben willst. Die selbst zu bauen, ist nicht ganz ohne, speziell wenn es im Mikrocontroller-Bereich vielleicht nicht geht, dass das Programm alle paar Sekunden für 100ms oder so stoppt oder du nicht so viel Speicher hast, dass du den einfachsten, aber speicherhungrigsten Stop&Copy-Algorithmus so runter programmieren kannst. Bleibt also Mark&Sweep oder Mark&Compact, wenn du nicht darauf angewiesen bist, dass Datenstrukturen ihre Adresse behalten müssen. Moderne GCs kombinieren verschiedene Grundalgorithmen. Vielleicht kannst du auch einfach den Weg gehen, den sie alle gehen und den Boehm GC nutzen. Ein eigener GC wird aber in der Regel effizienter sein. Natürlich könntest du auch Reference Counting benutzen, was vielleicht gerade im Microcontroller-Umfeld ein guter (wenn auch langsamerer) Weg wäre. Und hey, tausende Mac und iOS-Entwickler kommen damit auch klar.
Bei der statischen Typisierung würde ich empfehlen sehr pragmatisch vorzugehen und nicht zu versuchen, ein korrektes Typsystem zu entwickeln, insbesondere nicht, wenn dir co- und contravariant als Begriff nichts sagen. Habe einfach Container-Datentypen in denen du primitive Datentypen wie Zahlen explizit wrappen musst. Oder benutze nur Objekte - doch dann ist der Vorteil weg, den du dir wahrscheinlich durch statische Typisierung erhoffst.
Klassen sind (auch statisch) harmlos, ich würde allerdings auf die Mehrfachvererbung verzichten, weil der Linearisierungsalgorithmus von Python nicht trivial ist. Bei einfacher Vererbung ist es offenkundig trivial die Vorgänger abzulaufen.
Wenn du auf Effizienz wert legt, würde ich Python entschlacken, was die ganze "__"-Methoden angeht. Insbesondere würde ich "__dict__" weglassen und fordern, dass man für jede Klasse definieren muss, welche Variablen ihre Exemplare haben können.
Schließlich würde ich auch verbieten, dass man auf "toplevel" Code ausführen kann.
Geht das zu weit, möchte man also z.B. beim Laden eines Moduls erst die Klassen oder Funktionen erzeugen, würde ich den Weg gehen, den Pypy geht. Kompiliere nicht den Quelltext, sondern das, was entstanden ist, nachdem das Modul geladen wurde. Das erlaubt dann noch ein bisschen Metaprogrammierung.
Ein alternative Ansatz wäre, ein Makro-Konzept einzuführen, doch das ist bei Python nicht so einfach wie bei den ganzen Lisp-Dialekten, weil die Syntax viel komplexer ist. Das hat die Sprache Boo aber dennoch nicht daran gehindert, es trotzdem zu machen. Da diese Sprache aber syntaktisch für alles, was die .NET CLR so bietet, eine Antwort haben muss, finde ich die viel zu kompliziert.
Stefan