DSLs mit Tcl - oder warum YAML nicht automatisch menschenlesbar ist

DSLs mit Tcl - oder warum YAML nicht automatisch menschenlesbar ist
By Matthias Petermann / on 29.11.2025

Warum Menschenlesbarkeit mehr ist als Einrückung und Klammern

Wer Konfigurationen oder Datendateien von Hand pflegen muss, landet fast automatisch bei YAML, JSON oder XML. Alle drei sind etablierte Standards – und alle drei sind aus der Maschine heraus gedacht.

YAML:

  • whitespace-sensitiv
  • je nach Parser überraschend „magisch“

JSON:

  • Kommentare verboten
  • unflexibel für Menschen

XML:

  • überstrukturierte Tag-Landschaften

Der Anspruch lautet „menschenlesbar“, aber das Ergebnis ist oft das Gegenteil: viel Syntax bei wenig mentalem Modell.

Dabei gibt es eine Alternative, die fast niemand mehr auf dem Schirm hat.

Eine kleine, klar definierte DSL – implementiert als normaler Code in Tcl.

Keine Parserhölle, keine Bibliotheken, keine Überraschungen. Stattdessen: explizite Semantik und ein minimales, aber robustes Format.

ℹ️ Was ist eine DSL?

Eine Domain-Specific Language (DSL) ist eine kleine, klar abgegrenzte Sprache, die auf einen spezifischen Anwendungsbereich zugeschnitten ist – z. B. Buchhaltung, Build-Prozesse, Infrastrukturdefinition oder Automatisierungsregeln.

Im Gegensatz zu universellen Formaten wie YAML oder JSON ist eine DSL:

  • klein (nur die benötigten Konstrukte),
  • präzise (weniger Interpretationsspielraum),
  • auditierbar (Regeln sind explizit definiert),
  • robust (keine komplexen Parser notwendig).

DSLs reduzieren Komplexität, indem sie nur das ausdrücken, was in der Domäne erlaubt ist.


Warum Tcl ideal für kleine DSLs ist

ℹ️ Was ist Tcl?

Tcl (Tool Command Language) ist eine kompakte, interpretierte Sprache mit einem äußerst einfachen Kern: Alles besteht aus Befehlen und Argumenten. Der Interpreter ist klein, stabil und gut in C-Programme einbettbar – Faktoren, die Tcl bis heute in vielen Systemen präsent halten.

Wichtig für diesen Artikel: Tcl benötigt keine eigene Grammatik und kaum Syntax. Diese Reduktion macht die Sprache zu einem natürlichen Fundament für kleine, klar definierte DSLs.

Unter den heute gebräuchlichen Sprachen ist Tcl das einzige mir bekannte Beispiel, in dem sich DSLs mit extrem wenig Aufwand realisieren lassen – nicht weil Tcl „besser" wäre, sondern weil sein Sprachdesign Mini-Sprachen nahezu natürlich begünstigt.

Tcl ist radikal einfach gestaltet. Der Kern der Sprache passt auf eine Postkarte:

  • Jede Zeile ist ein Befehl.
  • Ein Befehl besteht aus Wörtern.
  • Das erste Wort ist der Name.
  • Der Rest sind Argumente.

Dadurch entsteht eine bemerkenswerte Eigenschaft:

Eine DSL sieht aus wie Konfiguration – ist aber gültiger Tcl-Code.

Beispiel:

user "alice"
port 8080
enable-feature caching

Drei einfache Prozeduren – und schon existiert eine kleine Sprache.


ℹ️ Was Tcl hier so besonders macht
  • Keine Grammatik
  • Kein Parsergenerator
  • Kein AST
  • Kein Overhead

Eine DSL entsteht, indem man einfach Befehle definiert, die danach wie Syntax aussehen.

Das ist menschenlesbar. Das ist auditierbar. Und es ist meilenweit entfernt vom YAML-Mikado.


Mini-DSL für Buchhaltung: Konten und Buchungen

Bleiben wir bei einem praktischen Beispiel: einer kleinen DSL für Buchungen im Sinne der doppelten Buchführung.

Ziel:

  • Konten definieren
  • Buchungen erfassen
  • am Ende eine einfache EÜR berechnen

DSL-Datei (buchungen.dsl):

# Konten
account Kasse        asset
account Bank         asset
account Erlöse_19    income
account Miete        expense
account Telefon      expense

# Buchungen (Soll an Haben)
booking 2025-01-03 Kasse      Erlöse_19 119.00 "Barverkauf"
booking 2025-01-04 Miete      Bank      800.00 "Büromiete"
booking 2025-01-10 Telefon    Bank       45.00 "Telefonrechnung"

Das liest sich wie eine Protokolldatei – ist aber komplett ausführbar.


Implementieren der DSL: zwei Befehle, kein Parser

array set ::accountType {}
set ::bookings {}

proc account {name type} {
    set ::accountType($name) $type
}

proc booking {date debit credit amount args} {
    set desc [join $args " "]
    lappend ::bookings [list $date $debit $credit $amount $desc]
}

Mehr braucht es nicht. Jede DSL-Zeile entspricht exakt einem Funktionsaufruf.


EÜR daraus erzeugen

proc compute_eur {} {
    array set eur {income 0.0 expense 0.0}

    foreach b $::bookings {
        lassign $b date debit credit amount desc

        if {[info exists ::accountType($debit)]
            && $::accountType($debit) eq "expense"} {
            set eur(expense) [expr {$eur(expense) + $amount}]
        }

        if {[info exists ::accountType($credit)]
            && $::accountType($credit) eq "income"} {
            set eur(income) [expr {$eur(income) + $amount}]
        }
    }

    puts "Einnahmen:      $eur(income)"
    puts "Ausgaben:       $eur(expense)"
    puts "Ergebnis:       [expr {$eur(income) - $eur(expense)}]"
}
🤔 Was ist eine EÜR?

Die Einnahmen-Überschuss-Rechnung (EÜR) ist ein einfaches Verfahren zur Ermittlung des steuerlichen Gewinns. Sie stellt lediglich Einnahmen und Ausgaben eines Jahres gegenüber:

  • Einnahmen (z. B. Erlöse, Honorare)
  • Ausgaben (z. B. Miete, Telefon, Material)

Der Gewinn ergibt sich als einfache Differenz:

Gewinn = Einnahmen – Ausgaben

Die EÜR eignet sich für kleinere Unternehmen und Selbständige, weil sie ohne Bilanzierung auskommt und auf tatsächlichen Zahlungsflüssen basiert.


Aber Moment: „Das ist doch Code? Ist das nicht gefährlich?“

Die Frage ist berechtigt – aber genau hier glänzt Tcl.

Tcl bietet einen eingebauten Safe Interpreter:

Eine Sandbox, in der nur explizit erlaubte Befehle existieren. Alles andere ist verboten.

Keine Systembefehle, kein Dateizugriff, kein Netzwerk, keine Nebenwirkungen.

Damit verwandelt sich die DSL in ein kontrolliertes Datenformat, auch wenn sie formal Code ist.


Sichere Ausführung im Safe Interpreter

Safe Interpreter erstellen

set safe [interp create -safe]

Nur unsere DSL-Befehle erlauben

interp alias $safe account {} account
interp alias $safe booking {} booking

DSL-Datei sicher ausführen

interp eval $safe [read [open $file]]

Was passiert, wenn jemand in die Datei schreibt:

exec rm -rf /

Antwort: Der Befehl existiert im Safe Interpreter nicht. Er ist schlicht nicht vorhanden.

Keine Angriffsmöglichkeit – keine Magie.


Lauffähiges Beispiel

TCL-Datei (calc.tcl):

#!/usr/bin/env tclsh

# DSL-Daten
array set ::accountType {}
set ::bookings {}

proc account {name type} {
    set ::accountType($name) $type
}

proc booking {date debit credit amount args} {
    lappend ::bookings [list $date $debit $credit $amount [join $args " "]]
}

proc compute_eur {} {
    array set eur {income 0.0 expense 0.0}
    foreach b $::bookings {
        lassign $b date debit credit amount desc
        if {[info exists ::accountType($debit)] && $::accountType($debit) eq "expense"} {
            set eur(expense) [expr {$eur(expense) + $amount}]
        }
        if {[info exists ::accountType($credit)] && $::accountType($credit) eq "income"} {
            set eur(income) [expr {$eur(income) + $amount}]
        }
    }
    puts "Einnahmen: $eur(income)"
    puts "Ausgaben:  $eur(expense)"
    puts "Ergebnis:  [expr {$eur(income) - $eur(expense)}]"
}

# Safe Interpreter
set safe [interp create -safe]
interp alias $safe account {} account
interp alias $safe booking {} booking

# Datei laden
set file [lindex $argv 0]
interp eval $safe [read [open $file]]

compute_eur

Test

$ ./calc.tcl buchungen.dsl
Einnahmen: 119.0
Ausgaben:  845.0
Ergebnis:  -726.0

Vergleich: gleiche Daten in YAML, JSON, XML

Format Lesbarkeit Werkzeugbedarf Überraschungen Kommentar
YAML ✖ mittel hoch hoch (Whitespace, Typen) komplex, versteckte Magie
JSON ✖ gering hoch mittel keine Kommentare, formal strikt
XML ✖ gering hoch gering überstrukturiert
Tcl-DSL ✔ hoch minimal sehr gering Befehle definieren, fertig

Die DSL gewinnt, weil sie weniger Syntax und mehr Semantik hat.


Wann DSLs bessere Datenformate sind

  • Wenn Menschen die Dateien wirklich lesen und schreiben sollen
  • Wenn Semantik wichtiger ist als Struktur
  • Wenn Auditierbarkeit entscheidend ist
  • Wenn Parserabhängigkeiten vermieden werden sollen
  • Wenn Konfigurationen eher „Sätze“ als „Objekte“ sind

Fazit: Kleine DSLs als echte Alternative zu YAML & Co.

Mini-DSLs erzeugen Formate, die explizit, knapp und auditierbar sind – ohne die Komplexität gängiger Standardformate. Tcl dient hier lediglich als Beispiel; entscheidend ist das Prinzip:

Eine DSL definiert Semantik direkt – ohne Parser-Schichten, ohne implizite Typen, ohne Überraschungen.

Wer Transparenz und Robustheit ernst nimmt, sollte DSLs wieder als ernsthafte Option betrachten.


💡 Wann Tcl eine besonders geeignete Wahl sein kann

Tcl spielt seine Stärken dort aus, wo Einfachheit, Klarheit und Verlässlichkeit wichtiger sind als modische Spracheigenschaften:

  • In eingebetteten Umgebungen: Tcl fügt sich nahtlos in C-Programme ein und eignet sich hervorragend als integrierte Skript- oder Konfigurationsschicht.
  • In langlebigen Systemen: Die Sprache ist außergewöhnlich stabil, vorhersehbar und frei von syntaktischen Überraschungen.
  • In schlanken Architekturen: Tcl benötigt keine Parser-Stacks, keine Generatoren, keine Zusatzwerkzeuge – Befehle sind die Sprache.
  • In Umgebungen mit begrenzten Ressourcen: Der Interpreter bleibt kompakt, portabel und trägt kaum Overhead in Software- oder Hardware-Budgets ein.
  • Wenn Konfiguration und Logik nicht getrennt werden sollen: DSLs können sowohl deklarativ als auch operational sein – ohne zusätzliche Template-Systeme oder Zwischenformate.
  • Wenn Auditierbarkeit wichtiger ist als syntaktische Mode: Tcl-DSLs bleiben transparent, linear und gut nachvollziehbar.

Kurz: Tcl glänzt überall dort, wo Robustheit, Einbettbarkeit und Einfachheit echten Wert haben.

📝 Proof statt PowerPoint

Der gezeigte Ansatz ist bewusst minimalistisch:

  • keine Kontenrahmenlogik
  • keine Verprobung
  • keine Periodenfilter
  • keine Belege

Es geht um das Prinzip: Wie man mit minimalem Aufwand ein lesbares und auditierbares Datenformat erhält – ohne YAML-Magie.

So entstehen robuste, transparente Strukturen – wenn man sie bewusst gestaltet.