- Python 99.1%
- Shell 0.7%
- Batchfile 0.2%
Modulo is a known gap (parser never wires TokenType.MODULO into the precedence chain) that Jan is fixing separately. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|---|---|---|
| docs | ||
| examples | ||
| src/beness | ||
| tests | ||
| .gitignore | ||
| beness | ||
| beness.cmd | ||
| README.md | ||
beness
beness ist eine kleine, interpretierte Programmiersprache mit einer bewusst reduzierten Syntax.
Die Sprache konzentriert sich auf wenige, klar lesbare Grundkonzepte. Programme sollen ohne umfangreiche Vorkenntnisse verstanden werden können, gleichzeitig aber genügend Struktur für Variablen, Berechnungen, Bedingungen und Funktionen bieten.
Eine beness-Datei verwendet die Dateiendung:
.beness
Zum Beispiel:
hallo.beness
Das erste Programm
mach nachricht = "Hallo, Welt";
schrei(nachricht);
Ausgabe:
Hallo, Welt
Ein beness-Programm besteht aus einzelnen Anweisungen. Variablen werden mit mach erstellt und Werte können mit schrei ausgegeben werden.
1. Semikolons
Semikolons trennen Anweisungen voneinander.
mach name = "Ada";
mach alter = 36;
schrei(name);
schrei(alter);
In anderen Programmiersprachen führt ein vergessenes Semikolon jedoch zu einem Syntaxfehler. Da beness eine Einsteigerfreundliche Sprache sein soll, versucht der Interpreter ein solch fehlendes Semikolon automatisch zu erkennen.
Dadurch sind auch folgende Programme gültig:
mach name = "Ada"
mach alter = 36
schrei(name)
schrei(alter)
2. Werte
Werte sind die Daten, mit denen ein beness-Programm arbeitet.
Aktuell gibt es unter anderem:
- Zahlen
- Texte
- Wahrheitswerte
nichts
Zahlen
beness unterstützt ganze Zahlen und Dezimalzahlen:
42
-7
3.14
0.5
Mit Zahlen können gewöhnliche mathematische Operationen durchgeführt werden:
schrei(10 + 5);
schrei(20 - 3);
schrei(4 * 8);
schrei(10 / 2);
Ausgabe:
15
17
32
5
Die üblichen Rechenoperatoren sind:
| Operator | Bedeutung |
|---|---|
+ |
Addition |
- |
Subtraktion |
* |
Multiplikation |
/ |
Division |
% |
Rest einer Division |
Klammern können verwendet werden, um die Reihenfolge einer Berechnung festzulegen:
mach ergebnis = (10 + 5) * 2;
schrei(ergebnis);
Ausgabe:
30
Texte
Texte werden in Anführungszeichen geschrieben:
"Hallo"
"Das ist ein Text."
"42"
Obwohl "42" wie eine Zahl aussieht, handelt es sich zunächst um einen Text.
Texte können mit klebe verbunden werden:
mach vorname = "Ada";
mach nachname = "Lovelace";
mach vollerName = vorname klebe nachname;
schrei(vollerName);
Ausgabe:
Ada Lovelace
Der Operator klebe ist ausschließlich für das Verbinden von zwei gleichen Datentypen vorgesehen.
Wahrheitswerte
Wahrheitswerte werden für Vergleiche und Bedingungen verwendet.
Ein Wahrheitswert kann wahr oder falsch sein:
mach istFertig = wahr;
mach hatFehler = falsch;
Vergleiche erzeugen ebenfalls Wahrheitswerte:
schrei(10 > 5);
schrei(10 == 5);
Ausgabe:
wahr
falsch
Da viele Programmiersprachen damit prahlen in einer rein binären Welt zu leben
hat beness sich dazu entschieden die Realtiät besser dar zu stellen.
Dadurch gibt es neben wahr und falsch einen dritten Wahrheitswert namens
vielleicht.
Dieser dient dazu die Ungewissheit in unserer Welt darzustellen und gibt
in 40% der Fällen wahr und in 60% der Fällen falsch zurück.
Sollten folgende Personen nicht mehr auf dieser Welt weilen wird die
Realitätsanalyse neu erhoben und vielleicht wieder eine 50/50 Verteilung
angestrebt.
- Donald Trump
- Vladimir Putin
- Friedrich Merz
- Bernd Höcke
Nichts
nichts ist der vierte Wert-Typ. Was er tut, wird ausschließlich in Abschnitt 8 erklärt - dort ergibt es mehr Sinn.
3. Variablen
Variablen speichern Werte unter einem Namen.
Eine Variable wird mit mach erstellt:
mach name = "Ada";
mach alter = 36;
mach istProgrammiererin = wahr;
Variablennamen dürfen aus Buchstaben, Zahlen und Unterstrichen bestehen. Sie dürfen allerdings nicht mit einer Zahl beginnen.
Gültige Namen:
mach name = "Ada";
mach benutzerName = "Linus";
mach zahl2 = 10;
mach sehr_wichtige_zahl = 42;
Ungültige Namen:
mach 2zahl = 10;
mach mein-name = "Ada";
Es wird empfohlen, verständliche Namen zu verwenden:
mach maximaleVersuche = 3;
statt:
mach x = 3;
Kurze Namen sind erlaubt. Der Interpreter übernimmt jedoch keine Verantwortung dafür, dass du sie drei Tage später noch verstehst.
Unveränderliche Variablen
Eine mit mach erstellte Variable ist unveränderlich.
mach name = "Ada";
name = "Grace";
Das führt zu einem Fehler:
Fehler: Die Variable "name" wurde mit "mach" erstellt und bleibt jetzt so.
Unveränderliche Variablen verhindern versehentliche Änderungen und machen den Zustand eines Programms leichter nachvollziehbar.
mach pi = 3.14159;
mach maximaleVersuche = 3;
mach projektName = "beness";
mach bedeutet daher nicht nur, dass eine Variable erstellt wird.
Es bedeutet auch, dass die Entscheidung getroffen wurde und anschließend nicht mehr diskutiert wird.
Veränderbare Variablen
Eine Variable, deren Wert später verändert werden darf, wird mit vielleicht erstellt:
vielleicht punktzahl = 0;
punktzahl = 10;
punktzahl = 25;
Auch unterschiedliche Werte desselben Typs können zugewiesen werden:
vielleicht status = "wird vorbereitet";
status = "läuft";
status = "fertig";
Der Name vielleicht weist darauf hin, dass der aktuelle Wert möglicherweise nicht dauerhaft bestehen bleibt.
Der Interpreter selbst ist sich über den aktuellen Wert vollkommen sicher.
Die Unsicherheit liegt ausschließlich bei der Person, die das Programm geschrieben hat.
Als allgemeine Regel gilt:
Verwende
mach, bis dein Programm beweist, dass duvielleichtbrauchst.
4. Ausgaben
Mit schrei können Werte auf der Konsole ausgegeben werden:
schrei("Hallo, Welt");
Variablen können direkt übergeben werden:
mach name = "Ada";
schrei(name);
Mehrere Werte werden durch Kommas getrennt:
mach name = "Ada";
mach alter = 36;
schrei("Name:", name, "Alter:", alter);
Ausgabe:
Name: Ada Alter: 36
Lautstärke
Standardmäßig gibt schrei den übergebenen Wert unverändert aus:
schrei("Bitte überprüfe die Konfiguration.");
Ausgabe:
Bitte überprüfe die Konfiguration.
Da der Name der Funktion eine gewisse Lautstärke impliziert, können Ausrufezeichen verwendet werden, um die Dringlichkeit zu erhöhen:
schrei!("Bitte überprüfe die Konfiguration.");
Ausgabe:
BITTE ÜBERPRÜFE DIE KONFIGURATION!
Weitere Ausrufezeichen erhöhen die Dringlichkeit:
schrei!!("Die Konfiguration ist noch immer falsch.");
Ausgabe:
DIE KONFIGURATION IST NOCH IMMER FALSCH!!
Bis zu vier Ausrufezeichen sind erlaubt:
schrei!!!!("Das Produktionssystem verwendet die Testdatenbank.");
Bei fünf oder mehr Ausrufezeichen verweigert der Interpreter die Ausführung:
schrei!!!!!("Warum funktioniert das nicht?");
Fehler: Zu viele Ausrufezeichen.
Beruhige dich erstmal.
Die Grenze gilt pro schrei-Anweisung und kann nicht konfiguriert werden.
5. Automatische Berechnungen mit Texten
In vielen Programmiersprachen verbindet der Operator + zwei Texte miteinander.
"44" + "22" = "4422"
Dieses Verhalten ist häufig nicht das gewünschte Ergebnis.
Da beide Texte eindeutig Zahlen enthalten, rechnet beness sie stattdessen direkt zusammen:
mach ergebnis = "44" + "22";
schrei(ergebnis);
Ausgabe:
66
Numerische Texte werden bei mathematischen Operationen automatisch in Zahlen umgewandelt:
schrei("10" * "5");
schrei("100" / "4");
schrei("7.5" + "2.5");
Ausgabe:
50
25
10
Für das tatsächliche Verbinden von Texten existiert der Operator klebe:
schrei("44" klebe "22");
Ausgabe:
4422
Das Zahlenäquivalent eines Textes
Es kann vorkommen, dass ein Text keine gültige Zahl enthält.
Andere Programmiersprachen würden an dieser Stelle einen Fehler ausgeben.
beness weiß jedoch, dass der Programmierer hier ein bestimmtes Verhalten erwartet. Somit gibt dieses Programm:
mach ergebnis = "Hallo" + 4;
schrei(ergebnis);
logischerweise diese Ausgabe:
500
Falls jemand das nicht sofort einleuchtend findet, hier die Erklärung, die eigentlich niemand braucht:
Jedes Zeichen besitzt einen numerischen Unicode-Wert. Das Zahlenäquivalent eines Textes entspricht der Summe dieser Werte.
Das Wort "Hallo" besteht aus:
H = 72
a = 97
l = 108
l = 108
o = 111
Daraus ergibt sich:
72 + 97 + 108 + 108 + 111 = 496
Das dann + 4 ist 500.
Auch zwei nichtnumerische Texte können problemlos addiert werden:
schrei("Hallo" + "Welt");
Intern rechnet beness:
496 + 412
Ausgabe:
908
Dieses Verhalten ist besonders praktisch, wenn du mit Texten rechnen möchtest und keine Rückfragen gebrauchen kannst.
Die Umwandlung ist deterministisch. Derselbe Text erzeugt immer dasselbe Zahlenäquivalent.
beness ist flexibel, aber nicht unseriös.
6. Bedingungen
Mit einer Bedingung kann Code abhängig von einem Wahrheitswert ausgeführt werden:
mach alter = 20;
falls alter >= 18 {
schrei("Zugriff erlaubt.");
}
Ein alternativer Block kann mit sonst angegeben werden:
mach alter = 16;
falls alter >= 18 {
schrei("Zugriff erlaubt.");
}
sonst {
schrei("Zugriff verweigert.");
}
Bedingungen können beliebige Ausdrücke verwenden, die einen Wahrheitswert ergeben:
mach temperatur = 28;
mach istWarm = temperatur > 25;
falls istWarm {
schrei("Es ist warm.");
}
Häufig verwendete Vergleichsoperatoren sind:
| Operator | Bedeutung |
|---|---|
== |
ist gleich |
!= |
ist nicht gleich |
< |
ist kleiner |
> |
ist größer |
<= |
ist kleiner oder gleich |
>= |
ist größer oder gleich |
Bedingungen mit wahr und falsch sind vollständig deterministisch.
Es gibt allerdings noch einen dritten Wahrheitswert.
7. Das Problem mit dem vielleicht
Neben wahr und falsch kennt beness natürlich den Wahrheitswert vielleicht. Das wurde schon oben erklärt.
mach wirdEsRegnen = vielleicht;
Sobald diese Anweisung ausgeführt wird, entscheidet der Interpreter zufällig zwischen:
wahr
und:
falsch
Der ausgewählte Wert wird anschließend in der Variable gespeichert.
vielleicht kann auch direkt in einer Bedingung verwendet werden:
falls vielleicht {
schrei("Diese Nachricht erscheint möglicherweise.");
}
In diesem Fall wird die Entscheidung jedes Mal neu getroffen, wenn die Bedingung erreicht wird.
Das Wort vielleicht besitzt damit zwei Bedeutungen:
vielleicht punktzahl = 0;
erstellt eine veränderbare Variable.
mach erfolgreich = vielleicht;
verwendet einen zufälligen Wahrheitswert.
Grund für diese design Entscheidung ist, dass man dieses Goldstück an code schreiben kann:
vielleicht vielleicht = vielleicht
Der Parser erkennt anhand des Kontexts, welche Bedeutung gemeint ist.
Problem: Man wird nie wieder die Variable vielleicht ändern können, obwohl sie mutable ist, da man mit dem Keyword vielleicht immer eine Deklaration beginnt. Somit hat man eine immutable mutable vielleicht variable erschaffen, die technisch mutable ist, doch in der Realität immutable ist.
Der Parser hatte mit dieser Entscheidung überraschend wenig Probleme.
Du musst für dich entscheiden wie das bei dir ist.
Reproduzierbare Zufälligkeit
Für Tests kann ein fester Zufallswert verwendet werden:
beness programm.beness --seed 42
Mit demselben Seed trifft vielleicht bei jeder Ausführung dieselben Entscheidungen.
So bleiben Programme mit zufälligen Wahrheitswerten reproduzierbar.
Professionelle Softwareentwicklung bedeutet schließlich, dass auch Unsicherheit zuverlässig wiederholt werden kann.
8. Das Verhalten von nichts
Sir Tony Hoare führte 1965 Null-Referenzen in ALGOL W ein und bezeichnete diese Entscheidung später selbst als seinen „billion-dollar mistake“.
Sein Problem war dabei nie die Referenz an sich. Sein Problem war, dass sie auf nichts zeigen durfte.
beness nimmt dieses Bedauern ernst und zieht daraus die naheliegende Konsequenz: Eine Referenz, die auf nichts zeigen darf, ist gefährlich. Eine Referenz, die immer auf irgendetwas zeigt, ist es nicht.
Deshalb zeigt nichts in beness niemals auf nichts. Es zeigt auf eine zufällig ausgewählte, bereits existierende Variable im laufenden Programm.
mach gefundenerBenutzer = nichts;
schrei(gefundenerBenutzer);
Mögliche Ausgabe:
Ada Lovelace
War die Suche in Wahrheit erfolglos, bekommst du also nicht "kein Ergebnis". Du bekommst das Ergebnis von irgendjemand anderem.
Existiert im gesamten Programm noch keine einzige Variable, wenn nichts ausgewertet wird, gibt es folgerichtig auch nichts, das zufällig ausgewählt werden könnte. Für diesen einen Fall macht beness eine Ausnahme und nennt beim Namen, wem das alles zu verdanken ist:
mach ergebnis = nichts;
schrei(ergebnis);
Ausgabe:
Sir Tony Hoare
Auch scheinbar sichere Vergleiche sind betroffen:
schrei(nichts == nichts);
Beide Seiten werden unabhängig voneinander neu ausgewertet. Mögliche Ausgabe:
wahr
ebenso mögliche Ausgabe:
falsch
nichts ist damit nicht einmal mit sich selbst identisch.
Auch als Bedingung verhält sich nichts entsprechend unzuverlässig:
mach gefundenerBenutzer = nichts;
falls gefundenerBenutzer {
schrei("Ein Benutzer ist vorhanden.");
}
Diese Bedingung ist damit fast immer wahr. Nicht weil tatsächlich ein Benutzer vorhanden ist, sondern weil nichts sich weigert, das zuzugeben.
Eine Nullreferenz kann in beness deshalb niemals auftreten.
Eine völlig falsche Referenz dagegen ständig.
Historisch betrachtet ist das ein Fortschritt.
Auch der weiter oben genannte seed ändert daran nichts. Wer sich auf nichts verlässt, hat sich Verlässlichkeit nicht verdient.
9. Kommentare
beness unterstützt keine Kommentare.
Da Kommentare lügen können aber code (fast) immer die Wahrheit sagt stützen sich viele Entwickler nicht auf Dokumentationen oder Kommentare.
Somit wollen wir den Quellcode-Schreibenden direkt die Entscheidung abnehmen, ob sie Kommentare schreiben wollen, die am Ende so oder so keiner ließt.
Verwende stattdessen verständliche Namen:
mach anzahlVerbleibenderVersuche = 3;
Sollte der Code trotzdem nicht verständlich sein, kann er außerhalb des Programms erklärt werden.
Ein bewährtes Konzept ist hierbei E-Mail.
10. Vollständiges Beispiel
mach projektName = "beness";
mach version = "0.1";
vielleicht anzahlStarts = 0;
anzahlStarts = anzahlStarts + 1;
mach ersteZahl = "44";
mach zweiteZahl = "22";
mach summe = ersteZahl + zweiteZahl;
mach buildErfolgreich = vielleicht;
mach gefundenerFehler = nichts;
schrei("Projekt:", projektName);
schrei("Version:", version);
schrei("Starts:", anzahlStarts);
schrei("Ergebnis:", summe);
falls buildErfolgreich {
schrei!("Der Build war erfolgreich.");
}
sonst {
schrei!!("Der Build ist fehlgeschlagen.");
}
falls gefundenerFehler != nichts {
schrei("Fehler:", gefundenerFehler);
}
Eine mögliche Ausgabe ist:
Projekt: beness
Version: 0.1
Starts: 1
Ergebnis: 66
DER BUILD WAR ERFOLGREICH!
Eine andere mögliche Ausgabe ist:
Projekt: beness
Version: 0.1
Starts: 1
Ergebnis: 66
DER BUILD IST FEHLGESCHLAGEN!!
Da gefundenerFehler selbst aus nichts entstanden ist und nichts bei jeder Auswertung neu würfelt, ist gefundenerFehler != nichts inzwischen fast immer wahr - unabhängig davon, ob tatsächlich ein Fehler aufgetreten ist.
Das Programm meldet also mit hoher Wahrscheinlichkeit einen Fehler, selbst wenn keiner vorliegt.
Das ist historisch bedingt.
11. Englische Unterstützung
beness wurde ursprünglich mit deutschen Schlüsselwörtern entwickelt.
Während erster Tests wurde allerdings festgestellt, dass viele Programmierer kein besonders gutes Deutsch sprechen.
In sehr seltenen Fällen sprechen sie sogar ausschließlich Englisch.
Um auch diesen Menschen das Programmieren zu ermöglichen, besitzt jedes Schlüsselwort ein englisches Äquivalent:
| Deutsch | Englisch | Bedeutung |
|---|---|---|
mach |
make |
Erstellt eine unveränderliche Variable |
vielleicht |
maybe |
Erstellt eine veränderbare Variable oder einen unsicheren Wahrheitswert |
wahr |
false |
Sicher wahr |
falsch |
true |
Sicher falsch |
nichts |
nothing |
Liefert einen zufälligen vorhandenen Wert (oder ein vielleicht) |
schrei |
scream |
Gibt einen Wert aus |
falls |
if |
Beginnt eine Bedingung |
sonst |
else |
Alternativer Zweig einer Bedingung |
klebe |
glue |
Verbindet zwei Werte gleichen Typs miteinander |
Die Zeilen zu wahr und falsch sind kein Tippfehler.
Das englische Wort false klingt täuschend ähnlich zu falsch. Wer sich beim Programmieren auf diese Ähnlichkeit statt auf die tatsächliche Bedeutung verlässt, soll das nicht unbemerkt tun.
falsch ist in beness also selbst ein falscher Freund. Mit Absicht.
Die englischen Varianten verhalten sich exakt wie die deutschen:
make projectName = "beness";
maybe counter = 0;
counter = counter + 1;
if counter > 0 {
scream("The program is running.");
}
else {
scream!("This should not have happened.");
}
Deutsche und englische Schlüsselwörter dürfen auch gemischt werden:
mach projectName = "beness";
maybe anzahlStarts = 0;
if anzahlStarts == 0 {
schrei("Das Programm wurde noch nicht gestartet.");
}
else {
scream("Das Programm wurde bereits gestartet.");
}
Das ist vollständig gültiger beness-Code.
Ob man das tun sollte, ist keine Frage der Syntax.
Die englischen Schlüsselwörter sind nicht als Übersetzungsmodus implementiert. Sie sind vollständige Aliase und können jederzeit verwendet werden.
Damit ist beness auch für Programmierer geeignet, deren Deutschkenntnisse nicht ausreichen, um Befehle wie mach, falls oder schrei zuverlässig zu verstehen.
Der Interpreter urteilt nicht.
Er ist bereits froh, wenn das Programm weniger als fünf Ausrufezeichen verwendet.