No description
  • Python 99.1%
  • Shell 0.7%
  • Batchfile 0.2%
Find a file
Jan-135 03d2855655 Remove modulo test cases
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>
2026-08-08 02:02:00 +02:00
docs Refactor project 2026-08-06 22:09:53 +02:00
examples Remove modulo test cases 2026-08-08 02:02:00 +02:00
src/beness Add binary math operators (+ - * / ** %) and test coverage 2026-08-08 01:55:51 +02:00
tests Refactor project 2026-08-06 22:09:53 +02:00
.gitignore Add verbose argument to cli and beness file as linux executable 2026-08-06 22:44:40 +02:00
beness Add verbose argument to cli and beness file as linux executable 2026-08-06 22:44:40 +02:00
beness.cmd Refactor project 2026-08-06 22:09:53 +02:00
README.md implement nichts / null reference. 2026-07-26 22:39:25 +02:00

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 du vielleicht brauchst.


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.