AND/OR/NOT, Fehler-Literale und Zeilen/Spalten einfügen/löschen
- AND(...)/OR(...)/NOT(...) als gewöhnliche Funktionen (kein Kurzschluss- Sonderfall wie IF/XLOOKUP nötig, werten wie in echtem Excel alle Argumente eager aus). Dabei die bisher dreifach duplizierte Wahrheits-Prüfung zu functions.ts#isTruthy() zusammengezogen, jetzt von FUNCTIONS.IF, evalIf() und dem Block-IF-Statement gemeinsam genutzt. Schließt "kein AND/OR für zusammengesetzte Bedingungen" aus den Bekannten Grenzen, auch nutzbar in "Mit Schleife"-IF-Bedingungen. - Neue Grammatik-Ergänzung: ErrorLiteralNode. Jeder der sieben Fehlercodes (#REF, #DIV/0!, #NAME?, #ERR, #CIRC, #VALUE!, #N/A) lässt sich jetzt direkt in eine Formel tippen (=IF(A1=0;#N/A;A1)) und wertet unbedingt zu genau diesem Fehler aus. - Zeilen/Spalten einfügen/löschen mitten im Gitter (Rechtsklick auf einen Zeilen-/Spaltenkopf), nicht nur am Rand wie bisher: Zellinhalte, Formate und Zeilenhöhen/Spaltenbreiten verschieben sich innerhalb der Tabelle, und jede Formel im gesamten Dokument, die die Tabelle referenziert (auch über Tabelle!Zelle/Blatt!Tabelle::Zelle-Qualifier aus anderen Tabellen), wird automatisch mitgezogen. Ein Bereich, der die Stelle überspannt, wächst/schrumpft entsprechend; eine Referenz, die exakt die gelöschte Zeile/Spalte trifft, wird zu einem echten #REF-Literal im Formeltext (nutzt die neue ErrorLiteralNode-Grammatik). $-Sperren bleiben dabei bewusst wirkungslos (anders als beim Kopieren), unveränderte Referenzen bleiben byte-identisch. Token-Level-Splice wie shiftFormula (Kopieren/ Ausschneiden), keine AST-Neuserialisierung. 406 Tests, vue-tsc/eslint/build sauber, Playwright-live-verifiziert. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+35
-2
@@ -27,6 +27,17 @@ function nums(values: CellValue[]): number[] {
|
||||
return out;
|
||||
}
|
||||
|
||||
/** The one truthy convention used everywhere in this engine a condition is
|
||||
* needed (IF's condition -- both the formula function and its real
|
||||
* short-circuit implementation in evaluator.ts's evalIf() --, a "Mit
|
||||
* Schleife" block's IF statement, and AND/OR/NOT below): a number is
|
||||
* truthy unless it's exactly `0`; text is truthy unless empty; an empty
|
||||
* cell (`null`) is never truthy. Exported so evaluator.ts can reuse the
|
||||
* exact same rule instead of duplicating it. */
|
||||
export function isTruthy(v: CellValue): boolean {
|
||||
return typeof v === 'number' ? v !== 0 : v !== null && v !== '';
|
||||
}
|
||||
|
||||
export const FUNCTIONS: Record<string, FunctionFn> = {
|
||||
SUM(values) {
|
||||
return nums(values).reduce((a, b) => a + b, 0);
|
||||
@@ -78,11 +89,30 @@ export const FUNCTIONS: Record<string, FunctionFn> = {
|
||||
}
|
||||
const [cond, thenValue, elseValue] = values;
|
||||
if (isError(cond)) return cond;
|
||||
const truthy = typeof cond === 'number' ? cond !== 0 : cond !== null && cond !== '';
|
||||
if (truthy) return thenValue;
|
||||
if (isTruthy(cond)) return thenValue;
|
||||
return values.length === 3 ? elseValue : 0;
|
||||
},
|
||||
|
||||
// Ordinary (non-short-circuit) functions -- like real Excel, EVERY
|
||||
// argument is evaluated regardless of the others (so e.g.
|
||||
// =AND(1=2; 1/0) is still #DIV/0!, not FALSE, exactly as in Excel) --
|
||||
// no special-casing in evaluator.ts needed, unlike IF/XLOOKUP. A range
|
||||
// argument flattens into one truthy check per cell, same as Excel.
|
||||
AND(values) {
|
||||
if (values.length === 0) return new FormulaError('#ERR', 'AND expects at least 1 argument');
|
||||
return values.every(isTruthy) ? 1 : 0;
|
||||
},
|
||||
|
||||
OR(values) {
|
||||
if (values.length === 0) return new FormulaError('#ERR', 'OR expects at least 1 argument');
|
||||
return values.some(isTruthy) ? 1 : 0;
|
||||
},
|
||||
|
||||
NOT(values) {
|
||||
if (values.length !== 1) return new FormulaError('#ERR', 'NOT expects exactly 1 argument');
|
||||
return isTruthy(values[0]) ? 0 : 1;
|
||||
},
|
||||
|
||||
// Dates/times are plain numbers here (see dates.ts) -- DATE/TIME build one,
|
||||
// TODAY/NOW read the current one. `null` (empty cell) arguments count as 0,
|
||||
// same as everywhere else in this engine; a non-numeric text argument is
|
||||
@@ -169,6 +199,9 @@ export const BUILTIN_SIGNATURES: ReadonlyArray<{ name: string; hint: string }> =
|
||||
{ name: 'CONCAT', hint: 'CONCAT(Text1; Text2; …)' },
|
||||
{ name: 'CONCATENATE', hint: 'CONCATENATE(Text1; Text2; …)' },
|
||||
{ name: 'IF', hint: 'IF(Bedingung; Dann; Sonst?)' },
|
||||
{ name: 'AND', hint: 'AND(Bedingung1; Bedingung2; …)' },
|
||||
{ name: 'OR', hint: 'OR(Bedingung1; Bedingung2; …)' },
|
||||
{ name: 'NOT', hint: 'NOT(Bedingung)' },
|
||||
{ name: 'DATE', hint: 'DATE(Jahr; Monat; Tag)' },
|
||||
{ name: 'TIME', hint: 'TIME(Std; Min; Sek)' },
|
||||
{ name: 'TODAY', hint: 'TODAY()' },
|
||||
|
||||
Reference in New Issue
Block a user