Kiste
EN

Standard-Bibliothek — `mathe`

Verfügbar — implementiert in Phase B.6.1 (erstes stdlib-Modul). Vollständige Spec in docs/mathe.md.

Die Standard-Bibliothek ist eine Sammlung von eingebauten Modulen, die du mit nutze aktivierst und über ihren Namen verwendest. Das erste fertige Modul ist mathe — alles, was die einfachen Operatoren + - * / % nicht können.

16.1 Wie ein eingebautes Modul aktiviert wird

nutze mathe                      // einbinden
sag mathe.PI                     // 3.141592653589793
sag mathe.wurzel(16)             // 4.0

Eingebaute Module wie mathe sind immer da — du brauchst keine Datei mathe.ki im Projekt. Du musst sie aber explizit mit nutze einbinden, damit Kiste weiss, dass du sie willst.

16.2 Konstanten

Name Wert Was es ist
mathe.PI 3.141592… Kreiszahl π
mathe.E 2.718281… Eulersche Zahl
mathe.TAU 6.283185…
mathe.UNENDLICH +∞ IEEE-754 +Unendlich
mathe.NEG_UNENDLICH -∞ IEEE-754 -Unendlich
mathe.NAN NaN „Keine Zahl" — IEEE-754

Konvention: Konstanten in GROSS-Schreibung, Funktionen in kleinschreibung. Das macht in Code sofort sichtbar, was sich nicht ändert.

16.3 Trigonometrie (alle Eingaben in Radian)

sag mathe.sinus(0)                  // 0.0
sag mathe.kosinus(mathe.PI)         // -1.0
sag mathe.tangens(mathe.PI / 4)     // ~ 1.0

// Inverse
sag mathe.arkussinus(1)             // PI/2
sag mathe.arkuskosinus(0)           // PI/2
sag mathe.arkustangens(1)           // PI/4
sag mathe.arkustangens2(1, 1)       // PI/4 (quadranten-bewusst)

// Grad ↔ Radian
sag mathe.als_radian(180)           // PI
sag mathe.als_grad(mathe.PI)        // 180.0

Wichtig: Trigonometrie-Funktionen geben immer komma zurück — Trigonometrie ist näherungsweise Mathematik, da gibt es kein „exakt".

16.4 Wurzel, Potenz, Logarithmus

sag mathe.wurzel(16)                // 4.0
sag mathe.n_te_wurzel(27, 3)        // ~3.0 (genau: 2.9999999999999996 — Float-Rundung)
sag mathe.hoch(2, 10)               // 1024  ← bleibt ganz!
sag mathe.hoch(2, -1)               // 0.5   ← negativer Exponent → komma
sag mathe.exp(1)                    // mathe.E
sag mathe.ln(mathe.E)               // 1.0
sag mathe.log10(1000)               // 3.0
sag mathe.log2(8)                   // 3.0
sag mathe.log(8, 2)                 // 3.0  ← Log mit beliebiger Basis

Spezialregel mathe.hoch: Bei zwei ganz-Argumenten und positivem Exponent bleibt das Ergebnis ganz (exakt, beliebig groß). Sonst wird es komma.

16.5 Min/Max, Absolutbetrag, Vorzeichen

sag mathe.maximum(3, 7, 2, 9)       // 9 — variadisch
sag mathe.minimum(3, 7, 2, 9)       // 2

// Liste? Mit ... entpacken!
nimm zahlen = [4, 11, 2, 8]
sag mathe.maximum(...zahlen)        // 11

sag mathe.absolut(-5)               // 5      (ganz bleibt ganz)
sag mathe.absolut(-1.5)             // 1.5    (dezimal bleibt dezimal)

sag mathe.vorzeichen(-7)            // -1
sag mathe.vorzeichen(0)             // 0
sag mathe.vorzeichen(99.5)          // 1

Wichtig: mathe.maximum und mathe.minimum brauchen mindestens 2 Argumente:

mathe.maximum(5)            // Fehler: mindestens 2 Argumente
mathe.maximum([3, 7, 2])    // Fehler: kann liste nicht vergleichen
mathe.maximum(...[3, 7])    // ok — Spread macht aus Liste 2 Argumente

Das ist Absicht: mathe.maximum(5) ist fast immer ein Tippfehler. Lieber laut scheitern als stumm 5 zurückgeben.

16.6 Zufall — eigenes Modul zufall

Zufalls-Funktionen leben nicht in mathe, sondern im eigenständigen Modul zufall. Siehe Kapitel 17.

16.7 Helfer für komma-Spezialwerte

sag mathe.ist_nan(mathe.NAN)         // wahr
sag mathe.ist_unendlich(mathe.UNENDLICH)   // wahr
sag mathe.ist_endlich(mathe.PI)            // wahr

Diese drei Helfer akzeptieren nur komma — bei ganz oder dezimal werfen sie einen Fehler. Begründung: ganz/dezimal sind per Definition endlich und nicht-NaN. Wer mathe.ist_nan(5) ruft, hat fast sicher einen Tippfehler — lieber scheitern als immer falsch zurückgeben.

16.8 Was nicht in mathe ist (und warum)

  • runden, aufrunden, abrunden, abschneiden, fast_gleich — sind globale Built-ins und bleiben das. Du nutzt sie ohne nutze mathe. Begründung: ein Symbol, ein Pfad. Es gibt kein mathe.runden als Alias.
  • mathe.maximum und mathe.minimum sind separate Funktionen — global gibt es länge etc., aber Min/Max nur in mathe.
  • kopiere, als_ganz, als_komma, als_dezimal, typ, doku — alle global.

16.9 Schatten-Warnung — wenn du eine Datei mathe.ki hast

Wenn du im Projekt-Ordner eine eigene Datei mathe.ki liegst und gleichzeitig nutze mathe schreibst, gewinnt das eingebaute mathe — deine lokale Datei wird ignoriert. kiste check warnt dich aber:

warning: lokale Datei .../mathe.ki wird vom eingebauten stdlib-Modul mathe
         überschattet — die lokale Datei wird ignoriert

Lösung: deine eigene Datei umbenennen (meine_mathe.ki) oder das eingebaute Modul bewusst nicht nutzen.

16.10 Alle Stdlib-Module auf einen Blick

Modul Tree-Walker + VM kiste build (LLVM-Pfad) Was es macht Kapitel
mathe verfügbar Subset seit 9.3.A Trigonometrie, Logarithmen, Min/Max, Konstanten §16
zufall verfügbar Subset seit 9.3.A Zufallszahlen, Mischen, Sampling, Reproduzierbarkeit §17
liste verfügbar kommt mit 9.3.B+ Higher-Order (gefiltert, abgebildet, gesammelt), Sortierung §18
text verfügbar Subset seit 9.3.B Strings transformieren, suchen, aufteilen §19
geld verfügbar vertagt (braucht Dezimal-Pfad) Exakte Geld-Arithmetik mit Währungen §20
datei verfügbar Subset seit 9.3.D Dateien lesen/schreiben, Verzeichnisse, Pfade §21
json verfügbar Subset seit 9.3.I.1 JSON parsen + serialisieren, Geld-Roundtrip §22
pfad verfügbar Subset seit 9.3.D Pfad-Manipulation, OS-Trennzeichen §23
umgebung verfügbar Subset seit 9.3.F OS-Info, Umgebungsvariablen, CLI-Args, exit §24
hash verfügbar Subset seit 9.3.C SHA-256, HMAC, Base64/Hex-Encoding §25
regex verfügbar Subset seit 9.3.E RE2-Pattern-Matching, Ersetzen, Zerlegen §26
csv verfügbar Subset seit 9.3.I.2 (Options-Karten in 9.3.I.3) CSV lesen + schreiben mit Optionen §27
zeit verfügbar Subset seit 9.3.F Datum/Zeit mit deutschen Format-Tokens, ISO-8601 §28
prozess verfügbar Subset seit 9.3.F Externe Programme starten, args-immer-als-Liste §29
bytes verfügbar Subset seit 9.3.C Binäre Daten, immutable, Datei-IO §30
netz verfügbar Subset seit 9.3.G HTTP-Client (GET/POST/PUT/DELETE/PATCH), TLS-Stufen §31
wert (LLVM-only-Modul) NEU seit 9.3.I.0 Heterogene-Werte: als_<typ>-Casts, ist_nichts, Skalar-Konstruktoren §16.10.7

16 Stdlib-Module sind komplett im Tree-Walker und VM, plus das LLVM-spezifische wert-Modul für heterogene Werte. Im nativen Pfad (kiste build) bringt Phase 9.3 die Module schrittweise — Stand 2026-05-14 (nach 9.3.I.2): mathe + zufall + text + bytes + hash + datei + pfad + regex + zeit + umgebung + prozess + netz + json + csv + wert (Subset) verfügbar (14 + 1 Foundation-Modul von 16), liste strukturell out-of-scope (Higher-Order/Closure-Strategie), geld vertagt (Dezimal-Pfad). Options-Karten für netz / prozess / csv kommen in 9.3.I.3.

16.10.0 Stdlib im kiste build-Pfad (seit 9.3.A)

Ab Phase 9.3.A kann der LLVM-Pfad mathe.* und zufall.* aufrufen — kein Boxing-Overhead, direkter cgo-Bridge-Call:

nutze mathe
nutze zufall

sag(mathe.wurzel(64f))          // 8.0
sag(mathe.absolut(-7.5f))       // 7.5
sag(mathe.vorzeichen(-3f))      // -1 (ganz)

zufall.setze_saat(42)
sag(zufall.zahl())              // reproduzierbar gleich zwischen `kiste run` und `kiste build`
sag(zufall.ganz(1, 6))          // wirft `Fehler` wenn min > max

Was im kiste build-Subset von 9.3.A funktioniert:

  • mathe (20 Funktionen): sinus/kosinus/tangens + Arkus-Varianten + arkustangens2, als_radian/als_grad, wurzel/exp/ln/log10/log2/log, absolut (komma), vorzeichen (komma→ganz), ist_nan/ist_unendlich/ist_endlich.
  • zufall (3 Funktionen): zahl, ganz (mit Throw bei min>max), setze_saat.

Seit 9.3.H zusätzlich:

  • mathe-Konstanten: mathe.PI, mathe.E, mathe.TAU, mathe.UNENDLICH, mathe.NEG_UNENDLICH, mathe.NAN als Compile-Time-Literale.
  • mathe.maximum(a, b, ...) und mathe.minimum(a, b, ...) variadic, ≥ 2 Komma-Args.

Aus dem kiste build-Subset noch ausgeschlossen (Tree-Walker akzeptiert weiterhin):

  • mathe.hoch(ganz, ganz) mit Ganz-Exponenten — Workaround: mathe.exp(mathe.ln(x) * y).
  • mathe.absolut(ganz) — Workaround: noch keine.
  • zufall.dezimal/zufall.element/zufall.gemischt/zufall.mische — Workaround: noch keine.

zufall.setze_saat gibt seit 9.3.H explizit Nichts zurück (keine Dummy-Ganz 0 mehr) — als bare Statement aufrufen, nimm x = zufall.setze_saat(42) ist Compile-Fehler in allen 3 Backends.

Implizite Ganz→Komma-Promotion funktioniert wie im Tree-Walker: mathe.wurzel(4) (ganz-Literal) klappt, weil der Compiler das Argument vor dem Bridge-Call auf komma hebt. Andere Konversionen (z.B. Text→Komma) sind harte Compile-Errors mit klarer deutscher Meldung.

16.10.1 text im kiste build-Pfad (seit 9.3.B)

Ab Phase 9.3.B kann der LLVM-Pfad 30 text.*-Funktionen rufen — Strings sind im nativen Build genauso komfortabel wie im Tree-Walker:

nutze text

nimm s = "  Hallo schöne Welt  "
sag(text.beschnitten(s))                          // "Hallo schöne Welt"
sag(text.kleingeschrieben(text.beschnitten(s)))   // "hallo schöne welt"
sag(text.umgekehrt("abcd"))                       // "dcba"
sag(text.ersetzt("a-b-c", "-", "+"))              // "a+b+c"
sag(text.länge("é"))                              // 1 (UAX#29 grapheme)

nimm parts = text.zerlegt("rot,grün,blau", ",")
wiederhole p in parts { sag(p) }                  // rot / grün / blau
sag(text.verkettet(parts, " | "))                 // "rot | grün | blau"

// finde_index returnt ?Ganz — Hit liefert Index, Miss liefert nichts
nimm idx = text.finde_index("Hallo Welt", "Welt")
sag(idx)                                          // 6
nimm fehl = text.finde_index("Hallo Welt", "Mond")
sag(fehl)                                         // nichts
sag(fehl ?? -1)                                   // -1 (??-Coalesce)

Was im kiste build-Subset von 9.3.B funktioniert — alle 30 Funktionen aus internal/stdlib/text/text.go; die 2-Arg-Trim-Formen (text.beschnitten(s, "xyz") etc.) sind seit 9.3.H zusätzlich im Subset:

  • Predicates/Reflection: länge, bytes_länge, zeichen_länge, leer, enthält, beginnt_mit, endet_mit, zähle, finde_index (returnt ?Ganz → Nichts bei Miss).
  • Transformationen: großgeschrieben, kleingeschrieben, kapitalisiert, beschnitten (Whitespace-Trim), links_beschnitten, rechts_beschnitten, umgekehrt, wiederholt, ersetzt, ersetzt_erstes.
  • Selection: scheibe, zeichen_bei, erste_n, letzte_n, gefüllt_links, gefüllt_rechts.
  • Split → liste<text>: zerlegt, zeilen, wörter, zeichen.
  • Join: verkettet (nimmt liste<text> + Trenner).

Throw-Verhalten wie im Tree-Walker: text.zähle(s, "") mit leerem Suchstring wirft Fehler, text.wiederholt(s, -1) analog, text.zeichen_bei(s, 99) bei Out-of-Range. Fangbar mit versuche/fange aus 9.2.E.

Aus dem kiste build-Subset noch ausgeschlossen (Tree-Walker akzeptiert weiterhin): keine spezifischen text-Items seit 9.3.H.

16.10.2 bytes + hash im kiste build-Pfad (seit 9.3.C)

Ab Phase 9.3.C kann der LLVM-Pfad mit binären Daten und kryptographischen Hashes arbeiten — Datei-Hashing, Base64/Hex-Encoding und HMAC-Auth-Tokens funktionieren in nativen Builds:

nutze bytes
nutze hash

// Bytes konstruieren, ansehen, in Hex/Base64 wandeln
nimm b = bytes.von_text("Hallo Kiste")
sag(b)                       // bytes(48616c6c6f204b69737465)
sag(länge(b))                // 11 (Byte-Anzahl, NICHT Grapheme)
sag(bytes.als_hex(b))        // 48616c6c6f204b69737465
sag(bytes.als_base64(b))     // SGFsbG8gS2lzdGU=

// Round-Trip via Hex
nimm back = bytes.als_text(bytes.aus_hex("48616c6c6f"))
sag(back)                    // Hallo

// SHA-256 als Hex
sag(hash.sha256("hello"))    // 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
sag(hash.sha256_bytes(b))    // Hash der Bytes (gleicher Algorithmus, Bytes-Input)

// HMAC für API-Authentifizierung
sag(hash.hmac_sha256("nachricht", "geheimer-schlüssel"))

// Base64 Roundtrip
sag(hash.base64_dekodiert(hash.base64_kodiert("Welt")))  // Welt

Was im kiste build-Subset von 9.3.C funktioniert:

  • bytes (17 Funktionen): Konstruktion (von_text, von_ganzen, aus_hex, aus_base64), Konversion (als_text, als_text_oder, als_ganze, als_hex, als_base64), Operationen (byte_bei mit negativen Indizes, scheibe, enthält, ist_gleich). Plus globale sag(b) (Format bytes(<hex>) mit Truncation für lange Sequenzen) und länge(b) (Byte-Count).
  • hash (16 Funktionen): md5/sha1/sha256/sha512 jeweils für Text + _bytes-Varianten für Binär-Input. hmac_sha256/hmac_sha512 plus _bytes-Varianten. base64_kodiert/base64_dekodiert und hex_kodiert/hex_dekodiert für Text-Roundtrips.

Throw-Verhalten wie im Tree-Walker — fangbar mit versuche/fange: bytes.von_ganzen([72, 999]) wirft (außer 0-255), bytes.aus_hex("abc") wirft (ungerade Länge), bytes.als_text(...) wirft bei ungültigem UTF-8, hash.base64_dekodiert("@@@") wirft bei ungültigem Base64.

Aus dem kiste build-Subset noch ausgeschlossen:

  • bytes.verkettet(...args) variadic — Tree-Walker erlaubt beliebig viele Bytes-Args. Polish 9.3.H löst variadic-Stdlib-Konvention. Workaround: Bytes via bytes.von_text(text.verkettet([bytes.als_text(a), bytes.als_text(b)], "")) zusammenführen (für UTF-8-Daten) oder weiterhin Tree-Walker für binäre Joins.

16.10.3 datei + pfad im kiste build-Pfad (seit 9.3.D)

Ab Phase 9.3.D kann der LLVM-Pfad mit Dateien und Pfaden arbeiten — CLI-Tools, Log-Verarbeitung, Config-File-Parser und Datei-Hash-Pipelines funktionieren nativ:

nutze datei
nutze pfad
nutze hash

// Datei schreiben + lesen
datei.schreibe("notiz.txt", "Hallo Kiste\n")
sag(datei.inhalt("notiz.txt"))            // Hallo Kiste
sag(datei.größe("notiz.txt"))             // 12

// Pfad zerlegen
sag(pfad.dateiname("a/b/c.txt"))          // c.txt
sag(pfad.erweiterung("c.txt"))            // .txt
sag(pfad.basisname("c.txt"))              // c

// Verzeichnisse + Iteration
datei.erstelle_ordner_rekursiv("logs/2026")
datei.schreibe(pfad.normalisiert("logs/2026/jan.log"), "...")
wiederhole e in datei.einträge("logs/2026") { sag(e) }   // jan.log

// Datei-Hash via bytes-Pfad (9.3.C + 9.3.D Cross-Module)
nimm b = datei.bytes_inhalt("notiz.txt")
sag(hash.sha256_bytes(b))

// Aufräumen
datei.lösche("notiz.txt")
datei.lösche_ordner_rekursiv("logs")

Was im kiste build-Subset von 9.3.D funktioniert:

  • datei (18 Funktionen):
    • Lesen: inhalt, zeilen (returnt liste<text>), bytes_inhalt (returnt Bytes für binäre Dateien).
    • Schreiben: schreibe, schreibe_bytes, hänge_an.
    • Prüfungen: existiert, ist_datei, ist_ordner.
    • Metadaten: größe (Byte-Count), geändert_unix (Unix-Timestamp als Ganz).
    • Manipulation: lösche, kopiere, verschiebe (mit Cross-Device-Fallback).
    • Verzeichnisse: erstelle_ordner, erstelle_ordner_rekursiv, lösche_ordner_rekursiv, einträge (sortiert).
  • pfad (10 Funktionen):
    • Zerlegung: dateiname, ordner, erweiterung, basisname, zerlegt (returnt liste<text>).
    • Normalisierung: normalisiert, absolut, relativ.
    • Prädikate: ist_absolut, ist_relativ.

Cross-Module-Verwendung mit bytes (9.3.C): datei.bytes_inhalt(p) returnt einen Bytes-Wert, der direkt an hash.sha256_bytes(b) oder bytes.als_hex(b) weitergereicht werden kann.

Throw-Verhalten wie im Tree-Walker — alle I/O-Operationen können werfen und sind fangbar mit versuche/fange: fehlende Dateien, Permission-Denied, nicht-UTF-8 bei datei.inhalt, ungültige Ziele bei kopiere/verschiebe.

Mutator-Funktionen (schreibe/lösche/kopiere/verschiebe/hänge_an/erstelle_ordner_*/lösche_ordner_rekursiv/schreibe_bytes) returnen seit 9.3.H in allen 3 Backends Nichts. Nutze sie als bare Statements — nimm x = datei.lösche(p) ist Compile-Fehler („Variable kann nicht auf einen Funktions-Aufruf gesetzt werden, der nichts zurückgibt").

Seit 9.3.H zusätzlich:

  • pfad.verbunden(a, b, ...) variadic mit ≥ 2 text-Args.
  • pfad.trenner / pfad.listen_trenner Konstanten.

Aus dem kiste build-Subset noch ausgeschlossen:

  • datei.geändert(path) → Zeit ist seit 9.3.F im Subset (Drift-Lift); datei.einträge_mit_details ist noch out-of-subset (heterogene Map-Werte, kommt mit 9.3.I).

16.10.4 regex im kiste build-Pfad (seit 9.3.E)

Ab Phase 9.3.E kann der LLVM-Pfad RE2-Pattern-Matching nativ. Pattern-Compile-Cache (sync.Map) ist transparent — repeated Calls mit gleichem Pattern sind günstig:

nutze regex

sag(regex.passt("^hello", "hello world"))   // wahr
sag(regex.gültig("a+b"))                    // wahr
sag(regex.gültig("a("))                     // falsch (ungültiges Pattern)

// treffer returnt ?text — Nichts wenn kein Match
nimm m = regex.treffer("\\d+", "abc123def")
sag(m)                                       // 123
sag(m ?? "kein-match")                       // 123 (Hit)

nimm fehl = regex.treffer("\\d+", "abcdef")
sag(fehl)                                    // nichts
sag(fehl ?? "kein-match")                    // kein-match

// Alle Treffer iterieren
wiederhole zahl in regex.treffer_alle("\\d+", "a1b22c333") { sag(zahl) }
// 1 / 22 / 333

// Ersetzen
sag(regex.ersetzt("\\d", "a1b2c3", "X"))     // aXbXcX
sag(regex.ersetzt_erstes("\\d", "a1b2c3", "X"))  // aXb2c3

// Zerlegen + Maskieren
wiederhole p in regex.zerlegt(",", "rot,grün,blau") { sag(p) }
sag(regex.maskiert("a.b(c)"))                // a\.b\(c\)

Was im kiste build-Subset von 9.3.E funktioniert (8 Funktionen):

  • passt(muster, text) → wahrheit — Match-Prüfung (CanThrow bei ungültigem Pattern).
  • gültig(muster) → wahrheit — Pre-Flight Pattern-Validation (non-throw).
  • treffer(muster, text) → ?text — erster Match oder Nichts (Nullable-Return).
  • treffer_alle(muster, text) → liste<text> — alle Matches.
  • ersetzt / ersetzt_erstes — Pattern-Replace.
  • zerlegt(muster, text) → liste<text> — String-Split per Pattern (Throw bei leerem Pattern).
  • maskiert(text) → text — escape meta-chars für literale Verwendung.

Aus dem kiste build-Subset noch ausgeschlossen:

  • regex.gruppen(muster, text) → ?liste<text> — braucht TypNullableListText der noch fehlt. Workaround: weiterhin Tree-Walker oder treffer für nur-erstes-Match.
  • regex.treffer_alle_gruppen(muster, text) → liste<liste<text>> — nested-list. Workaround: Tree-Walker oder iterative treffer_alle ohne Gruppen.

json und csv sind komplett aus dem 9.3.E-Subset draußen — siehe nutze json / nutze csv rejecten mit „Modul nicht registriert". Beide brauchen Type-System-Erweiterungen (heterogene Maps für JSON, nested Lists für CSV) die eine eigene Sub-Phase 9.3.I bekommen. Bis dahin: Tree-Walker / VM nutzen für JSON/CSV-lastige Programme.

16.10.5 zeit + umgebung + prozess im kiste build-Pfad (seit 9.3.F)

Ab Phase 9.3.F kann der LLVM-Pfad mit Zeitstempeln rechnen, Environment-Variablen lesen+schreiben und Sub-Prozesse starten — CLI-Tools, Build-Skripte, System-Automatisierung funktionieren nativ:

nutze zeit
nutze umgebung
nutze prozess

// Zeit-Operationen
nimm jetzt = zeit.jetzt()
sag(jetzt)                                  // 2026-05-13T10:42:00Z (Beispiel)
sag(zeit.jahr(jetzt))                       // 2026
sag(zeit.formatiert(jetzt, "JJJJ-MM-TT"))   // 2026-05-13

nimm geburt = zeit.von_komponenten(2024, 1, 15, 12, 0, 0)
sag(zeit.ist_vor(geburt, jetzt))            // wahr

// Environment
sag(umgebung.betriebssystem())              // windows
nimm pfad = umgebung.wert("PATH")
sag(pfad ?? "kein-PATH")

umgebung.setze("KISTE_DEMO", "hallo")
sag(umgebung.wert("KISTE_DEMO"))            // hallo
umgebung.entferne("KISTE_DEMO")

// Sub-Prozess (Windows-Beispiel)
nimm code = prozess.exit_code(["cmd", "/c", "exit", "0"])
sag(code)                                   // 0
sag(prozess.erfolgreich(["cmd", "/c", "exit", "0"]))  // wahr

Was im kiste build-Subset von 9.3.F funktioniert:

  • zeit (22 Funktionen): Konstruktion (jetzt/jetzt_lokal/heute/von_unix/von_unix_ms/von_komponenten), Parser (geparst_iso/geparst), 9 Komponenten-Getter (jahr/monat/tag/stunde/minute/sekunde/wochentag/unix/unix_ms), Formatieren (formatiert_iso/formatiert mit deutschen Tokens JJJJ/MM/TT/hh/mm/ss/…), Vergleich (ist_vor/ist_nach/ist_gleich), System (system_zone/schlafe_ms).
  • umgebung (14 Funktionen): env-vars (wert mit ?text-Return / wert_oder / allekarte<text> / setze / entferne), argumenteliste<text>, arbeitsverzeichnis/wechsle_in/programm_ordner, OS-Info (betriebssystem/architektur/benutzer/benutzer_ordner), beende(code).
  • prozess (3 von 5 Funktionen): ausgabe(argv)text (Throw bei exit≠0), exit_code(argv)Ganz, erfolgreich(argv)wahrheit. argv ist immer liste<text> mit args[0] = Programm-Name (security-cornerstone, kein Shell-Expansion).

Drift-Lift seit 9.3.F: datei.geändert(path) → Zeit funktioniert jetzt — gibt den File-Mtime als Zeit-Wert zurück (statt nur Unix-Timestamp). zeit.unix(datei.geändert(p)) liefert exakt das gleiche wie datei.geändert_unix(p).

Aus dem kiste build-Subset noch ausgeschlossen:

  • zeit.von_komponenten 3-Arg-Form ist seit 9.3.H im LLVM-Pfad verfügbar — zeit.von_komponenten(2026, 5, 13) setzt stunde/minute/sekunde auf 0 als Default (= Mitternacht UTC).
  • prozess.ausgeführt + prozess.ausgeführt_bytes (heterogene Karte als Return) — vertagt auf 9.3.I. Workaround: weiterhin Tree-Walker.
  • prozess-Options-Karte (2. Argument, heterogene Werte) — vertagt auf 9.3.I.
  • umgebung.argumente Source-Drift: TW nutzt interp.UserArgs(), LLVM-Pfad nutzt os.Args[1:] der laufenden Process — Inhalt unterschiedlich zwischen den zwei Pfaden.
  • LogicalOp mit CallExpression-Operanden (zeit.ist_vor(a, b) oder zeit.ist_gleich(a, b)) — Workaround: bind Bool-Results in Variablen.

länge(<Zeit>) ist bewusst rejected — Zeit ist kein Container. Wenn du Komponenten brauchst, nutze zeit.jahr/monat/tag/….

16.10.6 netz HTTP-Client im kiste build-Pfad (seit 9.3.G)

Ab Phase 9.3.G kann der LLVM-Pfad HTTP-Requests senden — REST-API-Clients, Download-Skripte, Status-Pings, einfache Web-Scraper laufen jetzt nativ als .exe:

nutze netz

// GET mit Antwort-Wert-Typ (status, inhalt, bytes, header, dauer_ms, url, erfolgreich)
nimm a = netz.hole("https://api.example.com/users")
sag(a.status)            // 200
sag(a.erfolgreich)       // wahr
sag(a.inhalt)            // JSON-String
sag(a.dauer_ms)          // 142

// Strict GET — wirft bei !erfolgreich oder !UTF-8
nimm text = netz.hole_text("https://api.example.com/info")
sag(text)

// POST mit text-Body
nimm post = netz.sende("POST", "https://api.example.com/echo", "hallo")
sag(post.inhalt)         // hallo

// POST mit bytes-Body (für Binär-Uploads)
nutze bytes
nimm bin = bytes.von_text("binärer-payload")
nimm up = netz.sende_bytes("POST", "https://api.example.com/upload", bin)
sag(up.status)

// HEAD-Status-Check
sag(netz.pinge("https://api.example.com"))  // 200

Was im kiste build-Subset von 9.3.G funktioniert:

  • netz (5 Funktionen): hole(url) → Antwort (GET), hole_text(url) → text (Strict GET, Throw bei !erfolgreich/!UTF-8), sende(method, url, text-body) → Antwort (POST/PUT/DELETE/PATCH/...), sende_bytes(method, url, bytes-body) → Antwort (Bytes-Body), pinge(url) → Ganz (HEAD-Status).
  • Antwort-Felder (7): a.status (Ganz), a.inhalt (text mit UTF-8-Validation), a.bytes (Bytes, identity-stable), a.header (karte<text> mit lowercased Keys, multi-values mit ", " joined), a.dauer_ms (Ganz), a.url (text), a.erfolgreich (wahrheit, 2xx).

Defaults sind hart-codiert (mirror Tree-Walker parseOptions):

  • 30s Request-Timeout.
  • Redirects folgen automatisch (max 10 Hops).
  • TLS-Zertifikat-Prüfung aktiv.
  • User-Agent: kiste-netz/0.3.

Body-Drift zu Tree-Walker — wichtig zu wissen:

Tree-Walker's netz.sende(method, url, body) akzeptiert body als text | bytes | nichts. Der LLVM-Pfad teilt das in zwei separate Funktionen:

  • sende(method, url, text-body) — text-Body
  • sende_bytes(method, url, bytes-body) — bytes-Body

Warum? Die LLVM-Stdlib-Registry hat single-typed Parameter-Slots (ParamTypes []KisteType) und unterstützt aktuell keine Multi-Type-Union-Parameter. Eine Erweiterung wäre Architektur-Aufwand außerhalb des 9.3.G-Scopes. Die Lift zur Single-Function-API kommt in 9.3.I.

Aus dem kiste build-Subset noch ausgeschlossen (alle vertagt auf 9.3.I):

  • netz.sende_json(method, url, daten[, opts]) — heterogene Daten als JSON-Body. Workaround: json.kodiere(daten) im Tree-Walker bauen, dann sende(method, url, json-text) aufrufen — oder vorerst kompletter TW/VM-Pfad.
  • Optionen-Karte (2. Arg von hole/pinge, 4. Arg von sende) mit header/parameter/zeitlimit_ms/folge_redirects/ca_datei/tls_pruefen — heterogene Map-Werte brauchen TypBoxedMapAny.
  • body = nichts (TW: kein Request-Body) — heute nicht im LLVM-Subset. Workaround: sende(method, url, "") schickt leeren Body mit Content-Length: 0.
  • a.header[k] ?? "default" Map-Index-Coalesce auf einem Field-Read-Receiver — klemmt im aktuellen IndexExpression-Pfad. Workaround: nimm h = a.header in eine Variable binden, dann h[k] ?? "default". Drift-Item für 9.3.H.

länge(<Antwort>) ist bewusst rejected — Antwort ist kein Container. Nutze länge(a.bytes) für die Body-Byte-Größe oder länge(a.header) für die Header-Count.

sag(<Antwort>) reproduziert das Tree-Walker-Format: antwort(<status>, <n> bytes, <ms>ms) — z.B. antwort(200, 1234 bytes, 142ms).

Seit 9.3.I.3.c zusätzlich (Options-Karten + sende_json):

nutze netz
nutze json
nutze wert

// Options-Karte als 2./4. Arg — header / parameter / zeitlimit_ms / folge_redirects
nimm a = netz.hole("https://api.example.com/user/42", {
    "header": {"Accept": "application/json", "Authorization": "Bearer xyz"},
    "parameter": {"include": "profile"},
    "zeitlimit_ms": 5000,
    "folge_redirects": wahr
})

// REST-API mit JSON-Body — neue sende_json-Funktion
nimm daten = {"name": "Sascha", "alter": 38, "aktiv": wahr}
nimm post = netz.sende_json("POST", "https://api.example.com/users", daten)
sag(post.status)            // z.B. 201

// JSON-Roundtrip: API gibt JSON zurück → parsen → Felder lesen
nimm parsed = wert.als_karte(json.geparst(post.inhalt))
sag(wert.als_text(parsed["id"]))

Options-Karte (mirror TW):

  • header: karte<text> mit Request-Headers. Default User-Agent kiste-netz/0.3 wenn nicht überschrieben.
  • parameter: karte<text> mit URL-Query-Parametern.
  • zeitlimit_ms: ganz (default 30000 = 30s). Bei Timeout wird ein Fehler geworfen.
  • folge_redirects: wahrheit (default wahr, max 10 Hops). falsch → 302/301 wird direkt zurückgegeben.

Drift im LLVM-Pfad (Drift-Backlog ohne hohe Priorität):

  • ca_datei und tls_pruefen sind im kiste build-Pfad heute no-op — die Option wird akzeptiert (kein Reject), hat aber keine Wirkung. TLS-Verifikation ist immer an. Tree-Walker spiegelt die TW-Variante mit stderr-Warning bei tls_pruefen=falsch; das kommt mit einem späteren TLS-Config-Lift.

netz.sende_json(method, url, daten[, opts]) akzeptiert beliebige Kiste-Werte (ganz, text, bool, komma, dezimal, Karte, Liste) als daten — encodet intern via json.erzeugt und setzt Default-Content-Type "application/json; charset=utf-8" (außer User hat einen eigenen Header gesetzt).

16.10.7 wert — heterogene Werte im kiste build-Pfad (seit 9.3.I.0)

Mit Phase 9.3.I.0 unterstützt der LLVM-Pfad heterogene Werte (beliebig-Typ) — der Daten-Typ, den JSON-/CSV-Parser und ähnliche Quellen produzieren. Im Tree-Walker repräsentiert das ein Go-interface{}; im LLVM-Pfad ist es der Slot-Type TypBoxedValue mit Tagged-Union-Payload (InnerTag aus {nichts, ganz, komma, dezimal, bool, text, liste, karte}).

Das wert-Modul ist der User-Eintrittspunkt für die Arbeit mit heterogenen Werten:

nutze wert

// Skalar-Konstruktoren — packe einen typisierten Wert in eine `beliebig`-Box
nimm v1 = wert.ganz(42)              // beliebig (innen ganz)
nimm v2 = wert.text("Sascha")        // beliebig (innen text)
nimm v3 = wert.bool(wahr)            // beliebig (innen bool)
nimm v4 = wert.nichts()              // beliebig (innen nichts)
nimm v5 = wert.komma(3.14f)          // beliebig (innen komma)
nimm v6 = wert.dezimal("0.1")        // beliebig (innen dezimal)

// Cast zurück auf konkrete Typen — wirft bei Tag-Mismatch
nimm n = wert.als_ganz(v1)           // ganz 42
nimm s = wert.als_text(v2)           // text "Sascha"
nimm b = wert.als_bool(v3)           // bool wahr

// Predicate für nichts-Test
wenn wert.ist_nichts(v4) {
    sag("ist nichts")
}

// typ(v) liefert den inneren Typ als text
sag(typ(v1))                          // "ganz"
sag(typ(v2))                          // "text"
sag(typ(v4))                          // "nichts"

// Display rekursiv — text in nested Containern in Quotes (mirror TW)
sag(v1)                               // 42
sag(v2)                               // Sascha
sag(v6)                               // 0.1

Was im kiste build-Subset von 9.3.I.0 funktioniert:

  • Konstruktoren (6): wert.nichts(), wert.ganz(n), wert.komma(f), wert.dezimal(t), wert.text(t), wert.bool(b).
  • Casts (7, alle CanThrow): wert.als_ganz(v), wert.als_komma(v), wert.als_dezimal(v), wert.als_text(v), wert.als_bool(v), wert.als_liste(v)liste<beliebig>, wert.als_karte(v)karte<beliebig>.
  • Predicate (1): wert.ist_nichts(v)bool.
  • Builtins: typ(v) returnt den InnerTag-Namen, sag(v) druckt rekursiv mit TW-Quoting, länge(als_liste(v)) und länge(als_karte(v)) zählen die Container-Slots.

Throw-Format bei Cast-Mismatch: wert.als_<wanted>: erwartet <wanted>, gefunden <found> — z.B. wert.als_ganz: erwartet ganz, gefunden text.

Seit 9.3.I.0.1.a zusätzlich (E-134.a, 2026-05-15):

  • Direct-Compare v == nichts / v != nichts — der häufigste Predicate-Pfad funktioniert jetzt ohne expliziten Cast:
    nimm a = json.versucht_geparst(eingabe)
    wenn a == nichts {
        sag("Parse-Fehler")
    } sonst {
        sag("OK")
    }
  • Konstruktive Diagnose für Direct-Ops auf BoxedValue (bv + 5, bv == 5, bv < 5, wenn bv, ...): User sieht jetzt eine konkrete Cast-Empfehlung statt der generischen „Operator nicht definiert"-Meldung. Beispiel: bv + 5 zeigt Cast zuerst: nimm n = wert.als_ganz(bv) dann n + .... Bei wenn bv sieht der User die Wahl zwischen Predicate (wenn nicht wert.ist_nichts(bv)) und Cast (wenn wert.als_bool(bv)).

Seit 9.3.I.0.1.b zusätzlich (E-134.b, 2026-05-16):

  • Direct-Coalesce v ?? d — Result-Type = Type-of-d (Q1.a). Compiler emittiert intern wenn ist_nichts(v) { d } sonst { wert.als_<typ-of-d>(v) }. Bei Inner-Tag-Mismatch wirft der Cast (z.B. bv ?? 0 mit bv=text("hi") wirft wert.als_ganz: erwartet ganz, gefunden text):
    nutze json
    nimm daten = json.geparst(eingabe)
    nimm karte = wert.als_karte(daten)
    nimm alter = karte["alter"] ?? 0          // Ganz-Default — als_ganz(bv)
    nimm name = karte["name"] ?? "unbekannt"   // Text-Default — als_text(bv)
  • Direct-Index v[k] und v[i] — Key-Typ entscheidet Cast-Pfad (Q2.a). Text-Key → Karten-Pfad. Ganz-Key → Listen-Pfad. Result ist immer wieder beliebig, daher funktionieren mehrstufige Ketten ohne Cast-Vorstufe:
    nimm daten = json.geparst(eingabe)
    nimm erster_name = daten["users"][0]["name"]   // Mehrstufig: Karte → Liste → Karte
    sag(wert.als_text(erster_name))
  • Out-of-Subset (mit konstruktiver Diagnose): Container-Defaults für bv ?? <liste> / bv ?? <karte> und bv ??= d bleiben Reject. Workarounds sind in der Diagnose genannt (wenn ist_nichts(bv) { d } sonst { ... } für Container-Defaults; bv = bv ?? d für ??=).

Seit 9.3.I.0.2 zusätzlich (E-134.c, 2026-05-16):

  • beliebig als Funktions-Parameter via Call-Site-Inferenz — Funktionen können heterogene Werte als Parameter empfangen. Der Multi-Pass-Solver leitet den Param-Type aus den Call-Sites her; primitive Args werden am Call-Boundary via wert.<X> automatisch geboxt (Q1.a):
    funktion log_wert(v) {
        sag(typ(v))
        sag(v)
    }
    
    log_wert(42)                  // Auto-Box: wert.ganz(42)
    log_wert("hi")                 // Auto-Box: wert.text("hi")
    log_wert(json.geparst(eingabe))   // Pass-through (schon beliebig)
  • beliebig als Funktions-Returngib <konkret> und gib nichts aus einer Funktion mit BoxedValue-Return werden implicit zu wert.<X>(...) bzw. wert.nichts() gewrappt (Q2.a):
    funktion feld_oder(d, k, default_wert) {
        gib d[k] ?? default_wert    // Direct-Index + Direct-Coalesce auf BoxedValue-Param-Variable
    }
    
    funktion vielleicht_zahl(t) {
        wenn länge(t) > 0 { gib wert.ganz(42) } sonst { gib nichts }
    }
  • Mehrstufige Daten-Verarbeitung über mehrere Funktionen läuft natürlich nativ — alle 9.3.I.0.1.a/b-Pfade (Direct-Compare, Direct-Coalesce, Direct-Index) funktionieren im Body eines BoxedValue-Param genauso wie auf einer Variable.

Seit 2026-06-11 zusätzlich: implizites Unboxing an Call-Grenzen (docs/llvm-implizit-unbox.md):

  • Die Gegenrichtung zum Auto-Box: ein beliebig-Wert (z.B. aus einer gemischten Karte) darf direkt in einen Parameter mit konkretem Typ fließen — der Compiler setzt den Laufzeit-Typcheck, genau wie der Tree-Walker:
    nimm person = {"name": "Sascha", "alter": 38}   // gemischte Karte → Werte sind beliebig
    funktion verdopple(n) { gib n * 2 }
    
    sag verdopple(3)                  // verankert n als ganz
    sag verdopple(person["alter"])    // beliebig → ganz: Laufzeit-Cast, läuft → 76
    // verdopple(person["name"]) würde ZUR LAUFZEIT werfen:
    // "wert.als_ganz: erwartet ganz, gefunden text (Zeile N, Spalte M)"
  • Kein wert.als_* mehr nötig für den 08/15-Fall — die Casts bleiben für explizite Kontrolle verfügbar. Gilt an allen Call-Grenzen (Funktionen, Closures, Methoden, Konstruktoren, super); Stdlib-Funktionen verlangen weiterhin den expliziten Cast (Overloads).

Aus dem 9.3.I-Subset noch rejected:

  • länge(<beliebig>) bleibt rejected — beliebig ist kein Container, nutze länge(wert.als_liste(v)) oder Analogon. Die Reject-Meldung enthält den Cast-Vorschlag.
  • Klassen-Instances als beliebig-Wert (eigene Tracer-Erweiterung; eigene Sub-Phase falls je User-Bedarf).
  • Mix f(beliebig) + f([1, 2]) (Container-Wert mixed mit BoxedValue) → Konflikt mit „widersprüchliche Typen". Workaround: explizit wert.als_liste(...) am Call-Site.

Container in 9.3.I.0: Heterogene Listen und Karten kommen produktiv mit 9.3.I.1 (json.geparst als natürlicher Produzent). Das interne _test_box-Modul liefert temporär die Konstruktoren für die Differential-Test-Suite — wird mit 9.3.I.1 obsolet.

16.10.8 json JSON-Parser + Serializer im kiste build-Pfad (seit 9.3.I.1)

Mit Phase 9.3.I.1 unterstützt der LLVM-Pfad JSON-Parsing und -Serialisierung als ersten produktiven Konsumenten der 9.3.I.0-Foundation. REST-API-Clients mit JSON-Bodies funktionieren jetzt nativ:

nutze netz
nutze json
nutze wert

// REST-API-Use-Case: GET → JSON-Parsing → Feld-Zugriff
nimm a = netz.hole("https://api.example.com/user/42")
nimm daten = json.geparst(a.inhalt)
nimm karte = wert.als_karte(daten)
nimm name = wert.als_text(karte["name"])
nimm alter = wert.als_ganz(karte["alter"])

sag(name)
sag(alter)

// Fehlende Felder produzieren BoxedValue(IVNichts) — User prüft via ist_nichts
nimm vielleicht_email = karte["email"]
wenn wert.ist_nichts(vielleicht_email) {
    sag("keine E-Mail im JSON")
} sonst {
    sag(wert.als_text(vielleicht_email))
}

// Encode mit pretty-print
nimm formatiert = json.erzeugt_schön(daten)
sag(formatiert)

Was im kiste build-Subset von 9.3.I.1 funktioniert:

  • json (4 Funktionen):
    • json.geparst(t)beliebig — wirft Fehler bei Parse-Fehler oder Big-Int-Overflow.
    • json.versucht_geparst(t)beliebig — bei Parse-Fehler wird BoxedValue(IVNichts) zurückgegeben (im LLVM-Pfad statt echtem nichts — siehe Drift-Hinweis unten). User prüft via wert.ist_nichts(v).
    • json.erzeugt(v)text — kompakt, kein Whitespace. Wirft bei NaN/Inf/zirkulärer Referenz.
    • json.erzeugt_schön(v[, einrückung])text — pretty-print mit Default 2 Leerzeichen oder eigener einrückung (z.B. "\t" für Tabs).
  • Map-Index auf karte<beliebig>: wert.als_karte(daten)["k"] returnt direkt beliebig (kein Nullable-Wrap). Missing-Key produziert BoxedValue(IVNichts).

Number-Heuristik beim Parsen (mirror docs/json.md §2.2):

  • JSON-Zahl enthält e/E (egal mit Punkt) → komma.
  • JSON-Zahl enthält . ohne e/Edezimal (kanonisiert: 42.042, 1.5001.5).
  • Sonst → ganz (wirft im LLVM-Pfad bei > int64-Range — TW hält exakt via BigInt).

Map-Reihenfolge: alphabetisch sortiert (mirror TW + encoding/json + RFC 8259's „unordered objects"-Semantik). Cross-Backend byte-identisch.

Drift zu Tree-Walker (wichtig zu wissen):

  • json.versucht_geparst bei Parse-Fehler: TW gibt echtes nichts-Value zurück, LLVM-Pfad gibt BoxedValue(IVNichts) zurück. Byte-identisch in sag-Output (beide drucken "nichts") und funktional identisch via wert.ist_nichts(v). Nur direkter Vergleich versucht_geparst(t) == nichts ist im LLVM-Pfad bis 9.3.I.0.1 rejected. Workaround: wert.ist_nichts(versucht_geparst(t)).
  • Big-Int-JSON-Zahlen (> 2^63-1 oder < -(2^63)): TW hält exakt via big.Int, LLVM-Pfad wirft Fehler bei Konversion. Workaround: solche Zahlen vorher als JSON-String parsen ("9223372036854775808" als String, dann wert.als_text).

Was im kiste build-Subset 9.3.I.1 NOCH NICHT funktioniert:

  • Direkter Vergleich bv == nichts → seit 9.3.I.0.1.a (E-134.a, 2026-05-15).
  • ?? default auf beliebig → seit 9.3.I.0.1.b (E-134.b, 2026-05-16) — primitive Defaults.
  • bv[k] direkt ohne wert.als_karte-Vorstufe → seit 9.3.I.0.1.b (E-134.b, 2026-05-16) — Text-Key + Ganz-Key.
  • bv ?? <container> mit Liste/Karte-Default und bv ??= d (Assign-Coalesce) bleiben Reject mit konstruktiver Diagnose.
  • beliebig als Funktions-Param oder Return → seit 9.3.I.0.2 (E-134.c, 2026-05-16) — Call-Site-Inferenz mit Auto-Box am Boundary.

16.10.9 csv CSV-Parser + Serializer im kiste build-Pfad (seit 9.3.I.2)

Mit Phase 9.3.I.2 unterstützt der LLVM-Pfad CSV-Parsing und -Serialisierung als zweiten produktiven Konsumenten der 9.3.I.0-Foundation neben json. Buchhaltungs-Exporte, Excel-Roundtrips, Daten-Imports laufen nativ:

nutze csv
nutze wert

// Header-basierter Parse für tabellarische Daten
nimm text = "name,alter,stadt\nSascha,38,Berlin\nClaude,2,Wolke"
nimm rows = wert.als_liste(csv.karten(text))

// Zugriff auf jede Zeile als Karte
nimm row0 = wert.als_karte(rows[0])
sag(wert.als_text(row0["name"]))    // "Sascha"
sag(wert.als_text(row0["alter"]))   // "38" (Cell-Type ist immer text)
sag(wert.als_text(row0["stadt"]))   // "Berlin"

// Fehlende Felder produzieren IVNichts — User prüft via ist_nichts
nimm vielleicht_email = row0["email"]
wenn wert.ist_nichts(vielleicht_email) {
    sag("keine E-Mail im CSV")
}

// Rohformat parsen (jede Zeile als Liste von Cells)
nimm raw = csv.zeilen("a,b\n1,2\n3,4")
sag(raw)
// → [["a", "b"], ["1", "2"], ["3", "4"]]

// Roundtrip parse → encode
sag(csv.erzeugt_aus_karten(csv.karten(text)))
// → "name,alter,stadt\nSascha,38,Berlin\nClaude,2,Wolke\n"

Was im kiste build-Subset von 9.3.I.2 funktioniert:

  • csv (4 Funktionen):
    • csv.zeilen(t)beliebig (Form: liste<liste<text>>) — wirft bei Parse-Fehler oder ungleicher Cell-Count pro Zeile.
    • csv.karten(t)beliebig (Form: liste<karte<text,text>>) — wirft bei leerem Input oder duplizierten Header-Spaltennamen.
    • csv.erzeugt(v)text — Cells dürfen text/ganz/komma/dezimal/bool sein (Auto-Konversion in CSV-Text mirror TW).
    • csv.erzeugt_aus_karten(v)text — Header aus erster Karte (Insertion-Order). Wirft wenn weitere Karten andere Key-Sets haben.

Defaults (mirror TW):

  • Trennzeichen: , (Komma)
  • Kommentar-Zeichen: keines
  • Behalte leere Zeilen: nein (encoding/csv schluckt sie sowieso)
  • CRLF-Zeilenumbrüche: nein (LF-only)

Map-Reihenfolge bei csv.karten: Anders als json.geparst (alphabetisch sortiert) definiert hier die Header-Zeile die Spalten-Order. Alle Karten haben identische Insertion-Order = Header-Order. Cross-Backend byte-identisch.

Cell-Type: alle CSV-Zellen werden als IVText zurückgegeben (mirror TW — string-only). Auto-Detect von Ganz/Komma als Drift-Backlog. Beim Encode dagegen werden gemischte Cell-Types unterstützt: wert.ganz(42)"42", wert.komma(3.5f)"3.5", wert.bool(wahr)"wahr".

Was seit 9.3.I.3.b zusätzlich funktioniert (Options-Karten als 2. Argument):

nutze csv

// Trennzeichen anpassen
nimm rows = csv.zeilen("a;b;c\n1;2;3", {"trenner": ";"})

// Kommentar-Zeilen ignorieren
nimm daten = csv.zeilen("# comment\nname,alter\nSascha,38", {"kommentar": "#"})

// CRLF-Zeilenumbrüche beim Encode
nimm csv_text = csv.erzeugt(rows, {"trenner": ";", "crlf_zeilenumbrüche": wahr})

// Tab-getrennte Werte
nimm tabs = csv.karten(t, {"trenner": "\t"})

Optionen (mirror TW):

  • Lesen (zeilen / karten): trenner (text, 1 Zeichen), kommentar (text/1-char oder nichts), behalte_leere_zeilen (Bool — heute no-op, mirror TW).
  • Schreiben (erzeugt / erzeugt_aus_karten): trenner (text, 1 Zeichen), crlf_zeilenumbrüche (Bool).

Heterogene Options-Karten funktionieren dank 9.3.I.3.a-Lift natürlich — gemischte Value-Typen (text + bool) in Karten-Literalen sind erlaubt. Auch eine reine {"trenner": ";"}-homogene Karte wird automatisch zu karte<beliebig> promotet am Call-Boundary (kein User-Workaround nötig).

Was im 9.3.I.3.b-Subset NOCH NICHT funktioniert:

  • Direct-Compare / Direct-Coalesce / Direct-Index auf beliebig — siehe §16.10.7 (gelift mit 9.3.I.0.1.a/b).

16.10.10 Regex-Gruppen + Datei-Details mit Karten-Werten (seit 9.3.I.4.a)

Mit Phase 9.3.I.4.a (E-132.a) lifted der LLVM-Pfad die letzten zwei regex-Funktionen + datei.einträge_mit_details — alle 10 TW-regex-Funktionen sind jetzt produktiv, und Verzeichnis-Listing mit Metadaten kommt nativ:

nutze regex
nutze wert

// regex.gruppen — `?liste<text>`-artige Semantik via TypBoxedValue
nimm g = regex.gruppen("(\\w+)=(\\d+)", "alter=38")
wenn nicht wert.ist_nichts(g) {
    nimm parts = wert.als_liste(g)
    sag(wert.als_text(parts[1]))   // "alter"
    sag(wert.als_text(parts[2]))   // "38"
}

// No-Match liefert nichts
nimm leer = regex.gruppen("(\\d+)", "abc")
sag(wert.ist_nichts(leer))   // wahr

// regex.treffer_alle_gruppen — liste<liste<text>>, Direct-Index
nimm pairs = regex.treffer_alle_gruppen("(\\w+)=(\\d+)", "a=1 b=22")
sag(pairs[0][1])   // "a"
sag(pairs[1][2])   // "22"
sag(länge(pairs))  // 2

// Leere äußere Liste bei No-Match (NICHT nichts — mirror TW)
sag(regex.treffer_alle_gruppen("xyz", "abc"))   // []

datei.einträge_mit_details listet ein Verzeichnis mit Metadaten pro Eintrag:

nutze datei
nutze wert

wiederhole eintrag in datei.einträge_mit_details("/tmp/projekt") {
    sag(wert.als_text(eintrag["name"]))          // text
    sag(wert.als_bool(eintrag["ist_ordner"]))    // wahr/falsch
    sag(wert.als_ganz(eintrag["größe"]))         // Bytes
    sag(wert.als_ganz(eintrag["geändert_unix"])) // Sekunden seit 1970
}

Sortierung: alphabetisch nach name (deterministic, mirror TW).

Subset-Stand 9.3.I.4.a:

  • regex.gruppen(p, t)beliebig (IVNichts bei No-Match, IVListe bei Match). Inner-Wrap via BoxedValue(IVText), nicht-matchende optionale Gruppen erscheinen als "".
  • regex.treffer_alle_gruppen(p, t)liste<liste<text>> (neuer Slot-Type TypBoxedListListText). Direct-Index t[i][j] ohne Cast. Empty-Match: leere outer-Liste.
  • datei.einträge_mit_details(pfad)liste<karte<beliebig>> (neuer Slot-Type TypBoxedListMapAny). Schlüssel: name (text), ist_ordner (Bool), größe (Ganz), geändert_unix (Ganz). Map-Index liste[i]["k"] produktiv.

Was seit 9.3.I.0.1.a/b zusätzlich nativ ist:

  • Direct-Compare regex.gruppen(...) != nichts ohne wert.ist_nichts-Cast — seit 9.3.I.0.1.a.
  • Direct-Index regex.gruppen(...)[0] ohne wert.als_liste-Vorstufe — seit 9.3.I.0.1.b.

16.10.11 Sub-Prozess-Output + IVBytes (seit 9.3.I.4.b)

Mit Phase 9.3.I.4.b (E-132.b) sind die letzten zwei prozess-Funktionen + ein neuer IVBytes-InnerTag für BoxedValue produktiv. Sub-Prozess-Aufrufe mit Zugriff auf stdout/stderr/exit_code laufen nativ:

nutze prozess
nutze wert

// Text-Variante: stdout/stderr sind text-Werte
nimm r = prozess.ausgeführt(["cmd", "/c", "echo", "hallo"])
sag(wert.als_text(r["stdout"]))    // "hallo\n"
sag(wert.als_ganz(r["exit_code"])) // 0

// Non-zero exit-code wird in der Karte sichtbar — KEIN Throw (anders als ausgabe)
nimm r2 = prozess.ausgeführt(["cmd", "/c", "exit", "7"])
sag(wert.als_ganz(r2["exit_code"])) // 7

Für binäres Output (gzip, Bild-Konvertierung, custom-Protokolle) gibt es die _bytes-Variante:

nutze prozess
nutze wert
nutze bytes

nimm r = prozess.ausgeführt_bytes(["gzip", "-c", "/tmp/data.txt"])
nimm body = wert.als_bytes(r["stdout"])   // bytes-Handle, kein UTF-8-Decode
sag(länge(body))                            // Byte-Count
sag(wert.als_ganz(r["exit_code"]))

Neuer IVBytes-InnerTag: wert.bytes(b) wraps einen bytes-Wert in einen BoxedValue. wert.als_bytes(v) peelt zurück. typ(v) returnt "bytes". sag(v) rendert byte-identisch zu sag(bytes-handle) (bytes(<hex>) mit 16-Byte-Cap + Truncation-Marker).

Subset-Stand 9.3.I.4.b:

  • prozess.ausgeführt(argv)karte<beliebig> mit BoxedValue-Inner (IVText für stdout/stderr, IVGanz für exit_code).
  • prozess.ausgeführt_bytes(argv) → identische Karte, aber stdout/stderr sind BoxedValue(IVBytes).
  • wert.bytes(b) + wert.als_bytes(v) neue Konstruktor + Cast.
  • IVBytes 9. InnerTag (8) im BoxedValue-System, voll integriert in Display/Tracer/Cast.

Was noch nicht funktioniert (Drift-Backlog 9.3.I.4.b):

  • Options-Karte ({arbeitsverzeichnis: ..., umgebung: ..., zeitlimit_ms: 5000}) als 2. Argument — Pattern aus 9.3.I.3.b/c wiederverwendbar, eigene künftige Mini-Sub-Phase.