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.
-
Hi Leute,
ich habe mal einen Versuch für eine kleine Syntax durchgeführt, diese sieht so aus:translation_unit ::= function function ::= "fn" identifier "(" ")" "->" type "{" statements "}" statements ::= (statement)* statement ::= variable_declaration | constant_declaration | loop | return_statement variable_declaration ::= "let" identifier ":" type "=" expression ";" constant_declaration ::= "const" identifier ":" type "=" expression ";" loop ::= "while" "(" condition ")" "{" statements "}" return_statement ::= "return" expression ";" expression ::= integer | string_literal | array_initialization | binary_expression array_initialization ::= "[" expression ";" expression "]" binary_expression ::= identifier operator expression condition ::= binary_expression identifier ::= /[a-zA-Z_][a-zA-Z0-9_]*/ integer ::= /\d+/ string_literal ::= /".*"/ operator ::= "<" | "=" | "+" | "*" | "/" type ::= "i64" | "string" | "[i64;" identifier "]"
einen Parser für diese Syntax ist auch fertig, wobei Parser ein zu großes Wort ist, ich würde eher sagen, er entscheidet ob ein String durch diese Grammatik produziert werden kann.
Hier kann man sich mal den Parser anschauen:
https://github.com/pmqtt/palladium/blob/main/src/Parser.cppAn euch mal eine Frage, wie würdet ihr gerne, eine Syntax für Arrays haben?
-
An euch mal eine Frage, wie würdet ihr gerne, eine Syntax für Arrays haben?
Nicht die C-Syntax Also die Klammern sollten nur direkt beim type stehen dürfen.
einen Versuch für eine kleine Syntax durchgeführt, diese sieht so aus
EBNF?
ich würde eher sagen, er entscheidet ob ein String durch diese Grammatik produziert werden kann
Ich glaube (TM), das ist sprachlich nicht richtig, ein String kann doch zur Grammatik passen oder nicht, aber er wird nicht durch diese "produziert"?
-
@Unbekannt sagte in Palladium:
Ich glaube (TM), das ist sprachlich nicht richtig, ein String kann doch zur Grammatik passen oder nicht, aber er wird nicht durch diese "produziert"?
Ja, das ist intuitiv etwas komisch, wenn man es vom Parsen der Sprache her betrachtet, aber in der Theoretischen Informatik nennt man das so. Die aufgelisteten Ersetzungsregeln nennt man auch "Produktionsregeln".
-
@Finnegan Danke für die Erklärung, dann war es vom Sprachgebrauch her doch richtig.
Mich hat nur der Vertausch von aktiv und passiv irritiert.