Forum: Projekte & Code smalldragon - a simple Transpiler to C


von pointbazaar (Gast)


Lesenswert?

Hallo Forum,

ich wuerde euch gerne kurz das smalldragon-Projekt vorstellen.

smalldragon ist eine einfache statisch typisierte Programmiersprache,
die nach C transpiliert werden kann.

Beispiel-Code:
1
//struct-field reordering will get applied to improve structure packing
2
struct Example {
3
    @private char a; //only accessible in this file
4
    uint32 b;
5
    char c;
6
    uint32 d;
7
}
8
fn main() ~> int {
9
    
10
    x = 2; //lokale variablen typinferenz
11
12
    if 0 < x < 3 { //comparison-operator-chaining
13
        x++;
14
    }
15
16
    uint8 y = 3; //fixed-width integer types
17
18
    for i in 0 .. x { //ranges
19
        y = subr((m)->m+1, x); //basic lambda support
20
    }
21
    return 0;
22
}
23
@deprecated //a few basic annotations
24
fn subr( ((int)->int) f, int k) -> int {
25
    return f(k);
26
}

Das Projekt ist nicht als Ersatz fuer C/C++ oder so gedacht,
sondern als Ergaenzung um schnell kleine Software-Module erstellen
zu koennen, und dennoch die Vorteile von C zu nutzen bezueglich
der Performance, Support fuer viele Architekturen, etc..

Bestimmte Features von C werden in smalldragon vorraussichtlich nicht
verfuegbar sein, wie etwa pointer-arithmetik, preprocessor, 
inline-assembly,
etc...

Ueber euer Feedback wuerde ich mich freuen :)

Projekt-Website: https://smalldragon.org/
Github: https://github.com/pointbazaar/smalldragon

Gruss, Alexander

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Schaut irgendwie danach aus, eine schöne, einfache Sprache wie C 
umständlicher zu machen :-)
Oder übersehe ich den genialen Ansatz?

: Bearbeitet durch User
von Andras H. (kyrk)


Lesenswert?

Ehrlich? Ist es nicht egal ob man
fn main() ~> int {
oder
int main(void) {
schreibt?

von (prx) A. K. (prx)


Lesenswert?

Andras H. schrieb:
> Ehrlich? Ist es nicht egal ob man

Die Grammatik von C ist kein genialer Wurf gewesen. Wenn jemand eine 
neue definiert, kann ich das nachvollziehen. Allerdings würde ich 
empfehlen, auch Variablendeklarationen mit einem Keyword einzuleiten, 
statt mit einem Typ. Dann sind Typnamen normale Identifier. Mit den 
später nachgereichten Typedefs ging in C der Spass los.

: Bearbeitet durch User
von Daniel A. (daniel-a)


Lesenswert?

Naja, mir gefällt reines C ehrlich gesagt besser.

Die Funktionsschreibweise mit fn ~> (wie in rust / go) ist etwas 
ungewohnt.

Bei der lokale variablen typinferenz habe ich etwas bedenken, weil man 
die Variable überhaupt nicht deklarieren muss. Wie ist dort der scope?
By python kann man schnell mal in sowas rein laufen:
1
#!python
2
def x(name):
3
  for name in ["test"]:
4
    pass
5
  return name # ist immer "test"
Kann einem hier sowas auch passieren? Sind die Variablen implizit 
Funktions oder Block scope? Hat die Sprache überhaupt blöcke und block 
scopes? Und hier: 
https://smalldragon.org/html/local-var-typeinference.html ist das 
wirklich "runtime cost" oder ist da "compiletime cost" gemeint?

Ein paar Beispiele enthalten while(true){}, aber anderenorts sind keine 
Klammern. Wäre while true {} nicht konsistenter? Oder ist das nur ein 
Dokumentationsartefakt?

Dann zu den Generics. Für mich sind das eher typ templates. Unter einem 
Generic würde ich eher sowas verstehen:
1
struct something_generic {
2
  int a;
3
  void* x;
4
  int b;
5
}
6
7
struct something_int {
8
  int a;
9
  int* x;
10
  int b;
11
}

Der Unterschied ist, something_int und something_generic sind gleich 
gross. Man kann struct something_int nach struct something_generic und 
zurück casten, wie in Java. Wäre x hier kein pointer, ist das nicht der 
fall. Das ist dann wie bei C++ templates.

Ich bin mir auch nicht sicher, wie sinnvoll es ist, die Funktionen als 
"Subroutinen" zu bezeichnen. Haben diese irgendwelche speziellen 
Semantiken? Kehren sie nicht immer zurück wie in ASM, oder haben sie 
keinen Stack für die Argumente wie Fortran, oder gibt es andere 
Besonderheiten bezüglich des control flows (können sie yieldn wie ein 
Generator, oder zwischeneinander switchen wie coroutinen?). Eine 
Funktion ist auch eine Subroutine, aber ist da irgend was was diese 
"Subroutinen" hier von Funktionen unterscheidet?

Der nächste Punkt wäre Modularität und Interfaces. Ich bin ein grosser 
Fan von Modularität und Interfaces. In C, jede Kompillationsunit ist 
unabhängig kompilierbar, und die Headerfiles stellen ein Interface dar, 
um alles zusammenzusetzen. Ist sowas hier noch machbar.

Das nächste wäre compile time reflection  introspection  Macros. Gibt 
es hier irgendws?

Und dann zum Schluss, naja, wie immer sind vor allem die Dinge nicht 
drin, die für mich eigentlich speziell interessant wären. Dinge die ich 
unter anderem gerne in meinen Programmen verwende sind:
 * Linktime linked lists, also globale unveränderbare Listen über 
mehrere Dateien verteilt, sowie optionale Implementierungen. Solches 
Zeugs: 
https://www.mikrocontroller.net/articles/C_Linkerhacks_%2B_Modularisierung
 * Interfaces. In C, im Grunde einfach ein Structtyp mit 
Funktionspointern, und ein Structtype einen Pointer darauf hat. Das 
Implementieren des Interface ist dann einfach, letzteres bei einem 
anderen Struct als member zu haben, und beim Instantiieren den Pointer 
auf die Implementation (das struct objekt mit den Funktionen drinn), zu 
setzen.
 * Mehr STL zeugs. Hashmaps, Dynamische Arrays, (Doubly) Linked Listen, 
eventuell ein generisches Listeninterface für alle. Eventuell was für 
reference counting, wie in C++ die smart pointer. Was man halt früher 
oder später so braucht.

Was auch toll wäre, ist aber eher Wunschdenken:
 * Exception handling. Es gibt ja viele Methoden, throw in C++, Optional 
/ tuple zeug in anderen Sprachen (also per typisiertem sentinel im 
Rückgabewert). Einfach was besseres als setjump...
 * Da kommt mir gerade noch der Gedanke, Optional, tuple zeug, 
typisierte Sentinelwerte. Sowas wäre schon praktisch.
 * async / await & Generatoren fände ich echt praktisch, aber leider 
schwierig umzusetzen. Aber Allgemein, primitive für event loops, warten 
auf input (eventuell bytes von Files, anderes Zeugs von anderen 
Programmteilen (pipes), etc.), managen von callbacks, solches zeugs, 
sind immer sehr nützlich. Leider aber auch sehr, sehr schwer nicht 
invasiv und gut umzusetzen.

: Bearbeitet durch User
von Daniel A. (daniel-a)


Lesenswert?

Oh, ich habe gerade gesehen, mit -> und ~> hast du eine Unterscheidung 
zwischen Idempotenten (ohne Seiteneffekte) und normalen Funktionen (mit 
Seiteneffekten).
Das Finde ich gut.

Und wie hast du eigentlich das halting Problem mit @halts gelöst?

von Jemand (Gast)


Lesenswert?

Daniel A. schrieb:
> Oh, ich habe gerade gesehen, mit -> und ~> hast du eine Unterscheidung
> zwischen Idempotenten (ohne Seiteneffekte) und normalen Funktionen (mit
> Seiteneffekten).
> Das Finde ich gut.

Der Unterschied zwischen „~>“ und „->“ ist beim überfliegen je nach Font 
viel zu klein, imo. Da fänd ich ein extra Keyword besser (meinetwegen 
„sub“ für Nebeneffekte und „fn“ für reine Funktionen?).

Ich lese die Doku so, dass die Typinferenz zur Laufzeit geschieht? Das 
wäre zumindest unituitiv, sofern da keine Variant-Konstrukte im Spiel 
sind.

Rückgabetypen nach den Namen, aber Variablentypen vor den Namen ist imo 
etwas inkonsistent. Zumal Variablentypen hinter den Namen eine deutlich 
sauberere Syntax für Inferenz ermöglicht.

Lambdas in C … Ein Traum ^^

Die Sprache hätte für mich aktuell allerdings wohl nicht genug „selling 
points“ um sie statt C zu benutzen und anderen Alternativen vorzuziehen.

Aber wenn ich das Projekt richtig verstehe, ist der Fokus aber auch gar 
nicht, die komplette Software in der Sprache zu schreiben. Dann wäre 
etwas Doku, wie man das Resultat dann in C/C++ einbindet wohl hilfreich.

von Jemand (Gast)


Lesenswert?

Ach ja: Gibt es überhaupt Arrays in irgendeiner Form? Braucht man ja 
hin- und wieder mal. In der Standardbibliothek gibt es auf jeden Fall 
Funktionen, die welche verarbeiten, aber nirgendwo was, wie man sie 
erstellt oder deklariert.

von Jemand (Gast)


Lesenswert?

Die Syntax zur Definition mit Typinferenz finde ich auch etwas 
unglücklich, da sie nicht von einer Zuweisung zu unterscheiden ist.

Aber ansonsten cooles Projekt mit interessanten Ideen. :)

von Yalu X. (yalu) (Moderator)


Lesenswert?

Endlich binden die bitweisen Operatoren &, ^ und | stärker als die
Vergleichsoperatoren, so wie es sein soll.

Ein paar Probleme gibt es noch mit Array-Überläufen bei langen
Variablen- und Typnamen, aber darauf hast du ja teilweise in Kommentaren
schon hingewiesen und wirst das sicher noch beheben.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Yalu X. schrieb:
> Endlich binden die bitweisen Operatoren &, ^ und | stärker als die
> Vergleichsoperatoren, so wie es sein soll.

Da kann ich nur zustimmen. In C/C++ ist das etwas unglücklich.

von Alexander H. (pointbazaar)


Lesenswert?

Andras H. schrieb:
> Ehrlich? Ist es nicht egal ob man
> fn main() ~> int {
> oder
> int main(void) {
> schreibt?

Hallo Andreas,
es ist fuer mich persoenlich klarer, den Typ:
1
 () ~> int
vom Name
1
 main
zu trennen und einfach nebeneinander zu schreiben, ohne beide zu
vermischen. Ist natuerlich Praeferenz Sache.

von Alexander H. (pointbazaar)


Lesenswert?

Daniel A. schrieb:
> Oh, ich habe gerade gesehen, mit -> und ~> hast du eine Unterscheidung
> zwischen Idempotenten (ohne Seiteneffekte) und normalen Funktionen (mit
> Seiteneffekten).
> Das Finde ich gut.
>
> Und wie hast du eigentlich das halting Problem mit @halts gelöst?

Hallo Daniel,

Vielen Dank fuer dein Feedback :)
@halts ist bei mir eine Annotation, die hier (vielleicht zu kurz) 
dokumentiert ist:
https://smalldragon.org/html/annotations.html

Natuerlich kann man nicht fuer jede Funktion entscheiden, ob sie 
terminiert
oder nicht.

Man kann @halts aber benutzen um einige wenige funktionen zu
annotieren, bei denen es sehr leicht ist zu zeigen, dass sie 
terminieren.
So leicht, dass das sogar der Transpiler uebernehmen kann.

Wenn smalldragon dann nicht zeigen kann, dass es terminiert, gibt's
ne kleine Fehlermeldung.

von Alexander H. (pointbazaar)


Lesenswert?

Jemand schrieb:
> Ach ja: Gibt es überhaupt Arrays in irgendeiner Form? Braucht man ja
> hin- und wieder mal. In der Standardbibliothek gibt es auf jeden Fall
> Funktionen, die welche verarbeiten, aber nirgendwo was, wie man sie
> erstellt oder deklariert.

Hallo,

Arrays gibt es konzeptionell schon, dynamisch alloziert mit malloc.
1
 
2
[char] arr = malloc(30);
3
arr[4] = 'k';
ist ein Beispiel.
In den Ordnern stdlib/ bzw. examples/ sollten sich weitere finden :)
Allerdings ist das momentan eher ein Kompromiss,
da ich das Konzept von sizeof noch nicht uebertragen habe.
Man muss sozusagen wissen, wie gross der datentyp ist, von dem man ein
Array allozieren will.

Gruss

von Alexander H. (pointbazaar)


Lesenswert?

Jemand schrieb:
> Daniel A. schrieb:
>> Oh, ich habe gerade gesehen, mit -> und ~> hast du eine Unterscheidung
>> zwischen Idempotenten (ohne Seiteneffekte) und normalen Funktionen (mit
>> Seiteneffekten).
>> Das Finde ich gut.
>
> Der Unterschied zwischen „~>“ und „->“ ist beim überfliegen je nach Font
> viel zu klein, imo. Da fänd ich ein extra Keyword besser (meinetwegen
> „sub“ für Nebeneffekte und „fn“ für reine Funktionen?).
>
> Ich lese die Doku so, dass die Typinferenz zur Laufzeit geschieht? Das
> wäre zumindest unituitiv, sofern da keine Variant-Konstrukte im Spiel
> sind.
>
> Rückgabetypen nach den Namen, aber Variablentypen vor den Namen ist imo
> etwas inkonsistent. Zumal Variablentypen hinter den Namen eine deutlich
> sauberere Syntax für Inferenz ermöglicht.
>
> Lambdas in C … Ein Traum ^^
>
> Die Sprache hätte für mich aktuell allerdings wohl nicht genug „selling
> points“ um sie statt C zu benutzen und anderen Alternativen vorzuziehen.
>
> Aber wenn ich das Projekt richtig verstehe, ist der Fokus aber auch gar
> nicht, die komplette Software in der Sprache zu schreiben. Dann wäre
> etwas Doku, wie man das Resultat dann in C/C++ einbindet wohl hilfreich.

Hallo,

den Unterschied von "->" zu "~>" beim Lesen ist etwas klein, da hast du 
Recht.
Das mit dem extra Keyword finde ich ist eine gute Idee.

Die Typinferenz passiert nicht zur Laufzeit, das war etwas ungluecklich
formuliert. Habe es umformuliert.

Du hast Recht, dass
1
 
2
uint x = 3;
3
fn main () ~> int {}
den Typ mit dem Bezeichner jeweils andersherum haben.
Das ist inkonsistent, allerdings habe ich mich da
bei den Variablen erstmal fuer die gaengige Konvention enschieden.

Wie meinst du, dass
>Zumal Variablentypen hinter den Namen eine deutlich
> sauberere Syntax für Inferenz ermöglicht.
?

Ja, genau, es ist dafuer gedacht dass man es alongside C
bzw auch einmalig verwenden kann.

Wenn man kurz etwas aufschreiben will, oder fuer eine
implementierung besonders viel mit Funktionspointern arbeitet,
dann kann man dafuer smalldragon verwenden,
einen C Code transpilieren, das ganze durch astyle jagen oder
per IDE so formatieren wie man will und dann einfach
nur noch mit dem C Code weitermachen, den man dann
verfeinern und optimieren kann.

Bzgl. der Beispiele hast du Recht, es sollte eine Anleitung geben,
wie man es zusammen mit C verwendet.


Danke fuer dein Feedback :)

von Dirk K. (merciless)


Lesenswert?

Hallo Alexander,

was genau ist deine Intention, SmallDragon zu
entwickeln/zu verwenden? Eine abstrahiertere
Sprache als C verwenden, um trotzdem andere
C-Libs leicht anbinden zu können? Performance-
Vorteile durch Benutzung etablierter C-Compiler?

Dazu habe ich auf den Webseiten nichts gefunden.

merciless

von Alexander H. (pointbazaar)


Lesenswert?

Dirk K. schrieb:
> Hallo Alexander,
>
> was genau ist deine Intention, SmallDragon zu
> entwickeln/zu verwenden? Eine abstrahiertere
> Sprache als C verwenden, um trotzdem andere
> C-Libs leicht anbinden zu können? Performance-
> Vorteile durch Benutzung etablierter C-Compiler?
>
> Dazu habe ich auf den Webseiten nichts gefunden.
>
> merciless

Hallo Dirk,

beide deine Punkte sind korrekt. ich will eine (fuer C Programmierer)
leicht verstaendliche aber abstrahiertere Programmiersprache bieten,
wo man sowas wie
- Lambdas,
- Lokale-Variablen Typinferenz,
- Generics(spaeter),
- try-catch Exception Handling
...
einfach verwenden kann, und immer weiss, was im Hintergrund passiert.

Momentan kann man die generierten C Sources dazu ja einfach einsehen.
z.b. try-catch verwendet einfach <setjmp.h> und der jmp_buf wird
als pointer weitergereicht.

Die Performance-Vorteile bei Verwendung etablierter C-Compiler
sind natuerlich beachtlich zur Alternative, etwa ein eigenes
Backend fuer x86 zu schreiben.

smalldragon soll leicht in existierende C-Projekte einzubinden sein,
und es soll auch einfach sein, C-Code in ein smalldragon Projekt 
einzubinden.

Es ist nicht stabil und hat bestimmt viele Bugs, aber wenn man die C 
Sources
einmal generiert hat, braucht man die smalldragon Sources ja auch nicht 
mehr ^^.

Allgemeine Ziele sowie Design-Entscheidungen sind hier zu finden:
https://smalldragon.org/html/project-goals.html

Allerdings wird diese Seite auch nicht jede Frage beantworten koennen :)
Daher immer gerne Fragen/Kritik.

Gruss

von Dirk K. (merciless)


Lesenswert?

Alexander H. schrieb:
> aber wenn man die C
> Sources
> einmal generiert hat, braucht man die smalldragon Sources ja auch nicht
> mehr ^^.
Du benutzt SmallDragon nur einmalig, um C-Sourcen
zu generieren? Und die Pflege erfolgt dann in den
C-Sourcen?

merciless

von Alexander H. (pointbazaar)


Lesenswert?

Dirk K. schrieb:
> Alexander H. schrieb:
>> aber wenn man die C
>> Sources
>> einmal generiert hat, braucht man die smalldragon Sources ja auch nicht
>> mehr ^^.
> Du benutzt SmallDragon nur einmalig, um C-Sourcen
> zu generieren? Und die Pflege erfolgt dann in den
> C-Sourcen?
>
> merciless

Hi Dirk,
so kann man es benutzen. Ich habe darauf geachtet dass die generierten
C Sources noch lesbar sind, die namen von variablen usw. werden 1:1 
uebernommen.
Wenn man einen anderen Style/Indentation haben will, kann man es ja noch 
durch astyle jagen :). Mit dem -h Flag kann man auch entsprechende 
Header generieren.

Ich habe es selbst nicht so benutzt, weil ich momentan eher an 
smalldragon
arbeite, anstatt an Projekten mit smalldragon (beides nebenher waere zu 
viel fuer mich).

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
Noch kein Account? Hier anmelden.