Commit Graph
4 Commits
Author SHA1 Message Date
wkhandClaude Sonnet 5 552f1cfae8 XLOOKUP als Formel, plus überspringbare optionale Argumente (;;)
Neue eingebaute Funktion XLOOKUP(Suchwert; Suchbereich; Rückgabebereich;
[WennNichtGefunden]; [Vergleichsmodus]; [Suchmodus]).

- Grammatik-Erweiterung (allgemein, nicht XLOOKUP-spezifisch): eine leere
  Argumentstelle zwischen zwei ";" (oder vor der schließenden ")") parst
  jetzt als neuer OmittedNode statt eines Parse-Fehlers -- Excels
  "dieses optionale Argument auslassen"-Idiom, z. B.
  XLOOKUP(x;a;b;;-1). Wertet überall wie eine leere Zelle aus (null);
  expand()/substitute() (userFunctions.ts) behandeln ihn als Blatt,
  collectRefs() ignoriert ihn stillschweigend.
- evaluator.ts: XLOOKUP wird -- wie IF -- vor dem generischen eager/
  flatten-Dispatch abgefangen (evalXLookup()), aus zwei Gründen: (1)
  WennNichtGefunden muss lazy ausgewertet werden (Kurzschluss wie bei
  IF), (2) Suchbereich/Rückgabebereich dürfen nicht in eine gemeinsame
  flache Werteliste geraten -- der generische Pfad würde die Grenze
  zwischen beiden Bereichen zerstören. Eigene xlookupEquals()
  (case-insensitiv/getrimmt für Text, kein Zahl-Text-Crossmatch) statt
  des `=`-Operators, der über toNumber() koerziert und nie auf Text
  träfe. Vergleichsmodus -1/1 (exakt/nächstkleiner/-größer, numerisch),
  Suchmodus 1/-1 (Richtung); Excels Platzhalter- und Binärsuch-Modi
  bewusst nicht unterstützt.
- functions.ts: Platzhalter-Eintrag für Namensreservierung (analog IF).
- types.ts: neuer Fehlercode #N/A (kein Treffer, kein
  WennNichtGefunden) -- dokumentierte Erweiterung, wie zuvor #VALUE!.
- collectRefs() brauchte keine Änderung -- sein generischer case 'func'
  läuft schon konservativ durch alle Argumente.

Tests: 22 neue (parser.spec.ts: OmittedNode leading/middle/trailing;
evaluator.spec.ts: SUM(1;;3)-Smoke plus 15 XLOOKUP-Fälle; workbook.spec.ts:
Neuberechnung bei Änderung in Such- ODER Rückgabebereich, keine bei
unbeteiligten Zellen; document.spec.ts: Namenskollision). 298 -> 318
Tests, alle grün. vue-tsc/eslint/build sauber. Live per Playwright
verifiziert (Treffer, #N/A, WennNichtGefunden, Kurzschluss-Beweis,
übersprungenes Argument für Vergleichsmodus, Neuberechnung nach Umbenennen
einer Suchbereich-Zelle inkl. nachträglich ausgelöstem #DIV/0! im vorher
nicht ausgewerteten WennNichtGefunden-Zweig).

READMEs aktualisiert (Funktionen, Fehlermodell, neue Abschnitte "Leere
Argumentstelle" und "Nachschlagen (XLOOKUP)" in engine/README.md,
Bekannte Grenzen, Testzahl; EBNF-Grammatik in parser.ts kommentiert).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 08:21:11 +02:00
wkhandClaude Sonnet 5 e6cb18ab3c Datum- & Zeit-Formatierung: Serial-Zahlen, Auto-Erkennung, DATE/TIME/TODAY/NOW
Wie in jeder Tabellenkalkulation ist ein Datum intern einfach eine Zahl
("Serial": Tage seit 1899-12-30, Uhrzeit als Bruchteil eines Tages) --
Datums-Arithmetik (=fällig - rechnungsdatum, =rechnungsdatum + 30)
funktioniert dadurch automatisch, ganz ohne Änderung an Operatoren.

- Neues Modul engine/dates.ts: dateToSerial/timeSerial (Bausteine für
  DATE()/TIME(), Überlauf wie Excel erlaubt), todaySerial/nowSerial
  (lokales Kalenderdatum), serialToUtcDate (Anzeige), parseDateTimeLiteral
  (Eingabe-Erkennung -- bewusst nur ISO/Punkt-Daten + Uhrzeit, KEINE
  Schrägstrich-Daten, da Tag-/Monat-zuerst über die unterstützten Locales
  nicht eindeutig auflösbar ist; ein zu einer ungültigen/rollierten
  Kalenderdatum führender Kandidat wird strikt verworfen statt lautlos
  zu überlaufen).
- workbook.ts: literalValue() erkennt Datum/Zeit vor dem Zahl-Test und
  liefert direkt den Serial.
- functions.ts: neue eingebaute Funktionen DATE(jahr;monat;tag),
  TIME(std;min;sek), TODAY(), NOW() -- TODAY/NOW sind bewusst nicht
  volatil (dokumentierte Grenze: diese Engine rechnet rein
  vorgänger-getrieben neu, ohne Timer).
- store/types.ts + format.ts: CellNumFmt um 'date'/'time'/'datetime'
  erweitert; formatValue rendert über Intl.DateTimeFormat (UTC-Zeitzone,
  pro Locale), 'plain' bekommt eine manuelle, locale-neutrale
  Formatierung statt der Browser-Locale.
- store/document.ts: setCellInput erkennt eine getippte Datums-/
  Zeit-Eingabe automatisch und setzt das passende Zellformat -- wie
  Excel/Numbers --, überschreibt aber nie ein bereits explizit gesetztes
  Format.
- Zahlenformat-Dropdown (SettingsPanel) und JSON-Validierung
  (isCellNumFmt) brauchten keine Änderung -- beide sind bereits generisch
  über NUM_FORMATS.

Tests: 32 neue (dates.spec.ts neu; workbook.spec.ts: literalValue +
DATE/TIME/TODAY/NOW-Formeln inkl. Datums-Arithmetik; format.spec.ts neu:
formatValue über mehrere Locales; document.spec.ts: Auto-Format beim
Eintippen, überschreibt nie ein gesetztes Format, Namenskollision mit
den neuen Funktionen). 265 -> 297 Tests, alle grün. vue-tsc/eslint/build
sauber. Live per Playwright verifiziert (Auto-Erkennung, Formel-Ergebnisse
ohne Auto-Format, Datums-Arithmetik, Schrägstrich-Daten bleiben Text).

READMEs aktualisiert (Funktionen, Zahlenformat, neuer "Datum & Zeit"-
Abschnitt in engine/README.md, Bekannte Grenzen, Testzahl; nebenbei eine
veraltete Aussage zu Vergleichsoperatoren in "Bekannte Grenzen" korrigiert
-- die gibt es seit der IF-Funktion bereits).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 23:24:39 +02:00
wkhandClaude Sonnet 5 ec85c371e8 IF-Funktion als Formel: echte Kurzschluss-Auswertung
Neue eingebaute Funktion IF(Bedingung; Dann; Sonst?), nutzbar in jeder
gewöhnlichen Zellformel (nicht nur in "Mit Schleife"-Funktionskörpern).

- functions.ts: IF als Platzhalter-Eintrag registriert, nur damit
  hasFunction('IF') greift (Namensreservierung -- ein Benutzer kann
  keine gleichnamige eigene Funktion mehr anlegen) und als defensiver
  Fallback. Trägt einen Kommentar, der auf die echte Implementierung
  verweist.
- evaluator.ts: die echte, kurzschließende Auswertung sitzt in
  evalScalar()s case 'func', VOR dem generischen eager-args-Pfad.
  evalIf() wertet nur die Bedingung immer aus, danach ausschließlich
  den genommenen Zweig -- der andere Zweig wird nie evaluiert, ein
  Fehler darin (z. B. =IF(B1=0; 0; A1/B1)) schlägt also nie durch.
  Gleiche Wahrheits-Konvention wie überall sonst im Engine (Zahl != 0
  bzw. nicht-leerer Text = wahr).
- collectRefs() bleibt unverändert: sein generischer case 'func' läuft
  bereits konservativ durch alle Argumente, unabhängig vom zur
  Laufzeit genommenen Zweig -- exakt wie Excels IF().

Tests: 8 neue (evaluator.spec.ts: beide Zweige, 2-/3-Argument-Form,
Kurzschluss-Beweis, Fehler in der Bedingung, Arity-Fehler, Vergleiche
als Bedingung; document.spec.ts: Namenskollision mit eingebautem IF).
257 -> 265 Tests, alle grün. vue-tsc/eslint/build sauber. Live per
Playwright verifiziert (inkl. Kurzschluss- und Neuberechnungsverhalten).

READMEs aktualisiert (Funktionen-Liste, Auswertungsmodell, Testzahl).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 23:00:45 +02:00
wkh c7885067e7 - init 2026-08-29 17:47:12 +02:00