Palladium
-
Hallo liebe Leute,
ich entwickele gerade zu lern Zwecken eine eigene Programmiersprache.
Das Ziel ist Konzepte zu verstehen und zu lernen.
Wo stehe ich gerade, ich habe einen Lexer entwickelt der eine beliebige anzahl an Tokens voraussehen kann.
Des Weiteren habe ich eine VM entwickelt, diese ist Stack und Registerbasiert.
Sie hat jetzt schon die Möglichkeit Speicher zu verwalten,
kann Funktionsaufrufe durchführen, bedingte und unbedingte Sprünge und natürlich kann sie addieren!
Wer lust hat mit mir in den Kaninchenbau tiefer einzusteigen ist total Willkommen. Hier der Link https://github.com/pmqtt/palladium
-
@c_new
Nee sorry, ich grusele mich immer noch vor dem GNU Bison.BTW: Wäre mal interresant welche moderenen Lexer es gibt.
-
@c_new
BTW:
Mach mir mal einen Gefallen. Wirf alle Makros raus. So was wieRETURN_IF_VALID
,ERROR_IF_CHAR
oderERROR_IF_CHAR_EQ
geht einfach nicht, da:- Kontrollfluß nicht ersichtlich ist
- Makros nicht debugbar sind
- Eine inline Funktion es auch macht
- Makros nicht typsicher sind. Welchen Typ hat z.B. F bei `RETURN_IF_VALID?
-
@Quiche-Lorraine
Ich ergänze:- Namespaces ignorieren
-
@Quiche-Lorraine
RETURN_IF_VALID durch eine Inline Funktion zu ersetzen ist nicht möglich! Genaus so gilt das für die anderen!
Das ist lustigerweise immer die Aussage von ChatGPT: Ersetze die Makros durch inline Funktionen. Da gibt es nichts zu ersetzen. Da die Makros ein if() return ersetzten.Aber danke für das Feedback.
-
@DocShoe Was meinst du mit namespaces ignorieren ?
-
@c_new Tatsächlich ist das ein Use-Case den ich auch auf Arbeit immer wieder im production Code finde. Ich mag es nicht. Glücklicherweise gibt es mit c++23 ein
std::expected
(oder auchstd::optional
- das hat entsprechende Operationen auch bekommen). Alternativ tun es auch andereexpected
Implementierungen von github.
Durch dessen monadic-functions (and_then
,or_else
, etc.), lassen sich diese Fälle häufig in die Sprache transferieren.Wenn ich mir z.B. das hier ansehe,...
if (opt_c) { RETURN_IF_VALID(lex_hex_number(opt_c, value)); RETURN_IF_VALID(lex_bin_number(opt_c, value)); RETURN_IF_VALID(lex_float_number(opt_c, value)); ERROR_IF_CHAR(opt_c, std::isdigit, "Invalid integer number"); _stream->prev(); }
EDIT:
Blödsinn folgt. Aber das zeigt nur, wie schwer das eigentlich zu greifen ist, was hinter deinen Macros steckt.
EDIT2: Korrigiertes folgt.... dann ließe sich das sich auch so lösen:
if (opt_c) { if (std::optional result = lex_hex_number(opt_c, value) .or_else([&]{ return lex_bin_number(opt_c, value); } .or_else([&]{ return lex_float_number(opt_c, value); }) { return // mach irgendwas mit deinem token } if (std::isdigit(*opt_c)) { return err("Invalid integer number"); // könnte man als std::unexpected modellieren } _stream->prev(); }
-
@DNKpp Lustigerweise ist RETURN_IF_VALID eine Prüfung für meine ResultOr<optional<Token>, Error> Klasse und diese prüft ja
if(res.ok()){ ....}return lex_hex_number(...).some( [&]( const ResultOr<std::optional<Token>,Error> & t)=> auto { if(!token) return lex_bin_number().some(...); return token; }
man könnte sich das so überlegen auf jedenfall! Aber hier stellt sich die Frage, ob so eine Verschachtelung besser zu verstehen ist.
Dann finde ich es besser:
auto res = lex_hex_number(...); if(res){ return res; } res = lex_bin_number(...); . . .
Da man die tiefe Verschachtelung vermeidet.