diff --git a/CHANGELOG.md b/CHANGELOG.md index c30eef4e3f..b8ee239d30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added helper methods for keeping track of cell/range dependencies: getCellPrecedents and getCellDependents. (#441) - Added 4 financial functions FV, PMT, PPMT, IPMT. - Added FORMULATEXT function. +- Added 8 information functions ISERR, ISNA, ISREF, NA, SHEET, SHEETS, ISBINARY, ISFORMULA (#481) ### Changed - Operation `moveCells` creating cyclic dependencies does not cause losing original formula. (#479) diff --git a/docs/guide/built-in-functions.md b/docs/guide/built-in-functions.md index ae88cded97..437ee8bf43 100644 --- a/docs/guide/built-in-functions.md +++ b/docs/guide/built-in-functions.md @@ -81,14 +81,22 @@ lets you design your own [custom functions](custom-functions). | DELTA | Engineering | Returns TRUE (1) if both numbers are equal, otherwise returns FALSE (0). | DELTA(Number_1; Number_2) | | ERF | Engineering | Returns values of the Gaussian error integral. | ERF(Lower_Limit; Upper_Limit) | | ERFC | Engineering | Returns complementary values of the Gaussian error integral between x and infinity. | ERFC(Lower_Limit) | +| ISBINARY | Information | Returns TRUE if provided value is a valid binary number. | ISBINARY(Value) | | ISBLANK | Information | Returns TRUE if the reference to a cell is blank. | ISBLANK(Value) | -| ISERROR | Information | The ISERROR tests if the cells contain general error values. | ISERROR(Value) | +| ISERR | Information | Returns TRUE if the value is error value except #N/A!. | ISERR(Value) | +| ISERROR | Information | Returns TRUE if the value is general error value. | ISERROR(Value) | | ISEVEN | Information | Returns TRUE if the value is an even integer, or FALSE if the value is odd. | ISEVEN(Value) | +| ISFORMULA | Information | Checks whether referenced cell is a formula. | ISFORMULA(Value) | | ISLOGICAL | Information | Tests for a logical value (TRUE or FALSE). | ISLOGICAL(Value) | +| ISNA | Information | Returns TRUE if the value is #N/A! error. | ISNA(Value) | | ISNONTEXT | Information | Tests if the cell contents are text or numbers, and returns FALSE if the contents are text. | ISNONTEXT(Value) | | ISNUMBER | Information | Returns TRUE if the value refers to a number. | ISNUMBER(Value) | | ISODD | Information | Returns TRUE if the value is odd, or FALSE if the number is even. | ISODD(Value) | -| ISTEXT | Information | Returns TRUE if the cell contents refer to text.| ISTEXT(Value) | +| ISREF | Information | Returns TRUE if provided value is #REF! error. | ISREF(Value) | +| ISTEXT | Information | Returns TRUE if the cell contents refer to text. | ISTEXT(Value) | +| SHEET | Information | Returns sheet number of a given value or a formula sheet number if no argument is provided. | SHEET([Value]) | +| SHEETS | Information | Returns number of sheet of a given reference or number of all sheets in workbook when no argument is provided. | SHEETS([Value]) | +| NA | Information | Returns #N/A! error value.| NA(Value) | | FV | Financial | Returns the future value of an investment. | FV(Rate; Nper; Pmt[; Pv;[ Type]]) | | IPMT | Financial | Calculates the interest portion of a given loan payment in a given payment period. | IPMT(Rate; Per; Nper; Pv[; Fv[; Type]]) | | PMT | Financial | Returns the periodic payment for a loan. | PMT(Rate; Nper; Pv[; Fv[; Type]]) | diff --git a/src/i18n/languages/csCZ.ts b/src/i18n/languages/csCZ.ts index d7e7192635..68f8a25812 100644 --- a/src/i18n/languages/csCZ.ts +++ b/src/i18n/languages/csCZ.ts @@ -77,13 +77,18 @@ const dictionary: RawTranslationPackage = { INDEX: 'INDEX', INT: 'CELÁ.ČÁST', IPMT: 'PLATBA.ÚROK', + ISBINARY: 'ISBINARY', ISBLANK: 'JE.PRÁZDNÉ', + ISERR: 'JE.CHYBA', ISERROR: 'JE.CHYBHODN', ISEVEN: 'ISEVEN', + ISFORMULA: 'ISFORMULA', ISLOGICAL: 'JE.LOGHODN', + ISNA: 'JE.NEDEF', ISNONTEXT: 'JE.NETEXT', ISNUMBER: 'JE.ČISLO', ISODD: 'ISODD', + ISREF: 'JE.ODKAZ', ISTEXT: 'JE.TEXT', LEFT: 'ZLEVA', LEN: 'DÉLKA', @@ -101,6 +106,7 @@ const dictionary: RawTranslationPackage = { MMULT: 'SOUČIN.MATIC', MOD: 'MOD', MONTH: 'MĚSÍC', + NA: 'NEDEF', NOT: 'NE', ODD: 'ZAOKROUHLIT.NA.LICHÉ', OFFSET: 'POSUN', @@ -119,6 +125,8 @@ const dictionary: RawTranslationPackage = { ROUNDUP: 'ROUNDUP', ROWS: 'ŘÁDKY', SEARCH: 'HLEDAT', + SHEETS: 'SHEETS', + SHEET: 'SHEET', SIN: 'SIN', SPLIT: 'SPLIT', SQRT: 'ODMOCNINA', diff --git a/src/i18n/languages/daDK.ts b/src/i18n/languages/daDK.ts index 81e54b65fa..d1d824b767 100644 --- a/src/i18n/languages/daDK.ts +++ b/src/i18n/languages/daDK.ts @@ -77,13 +77,18 @@ const dictionary: RawTranslationPackage = { INDEX: 'INDEKS', INT: 'HELTAL', IPMT: 'R.YDELSE', + ISBINARY: 'ISBINARY', ISBLANK: 'ER.TOM', + ISERR: 'ER.FJL', ISERROR: 'ER.FEJL', ISEVEN: 'ER.LIGE', + ISFORMULA: 'ER.FORMEL', ISLOGICAL: 'ER.LOGISK', + ISNA: 'ER.IKKE.TILGÆNGELIG', ISNONTEXT: 'ER.IKKE.TEKST', ISNUMBER: 'ER.TAL', ISODD: 'ER.ULIGE', + ISREF: 'ER.REFERENCE', ISTEXT: 'ER.TEKST', LEFT: 'VENSTRE', LEN: 'LÆNGDE', @@ -101,6 +106,7 @@ const dictionary: RawTranslationPackage = { MMULT: 'MPRODUKT', MOD: 'REST', MONTH: 'MÅNED', + NA: 'IKKE.TILGÆNGELIG', NOT: 'IKKE', ODD: 'ULIGE', OFFSET: 'FORSKYDNING', @@ -119,6 +125,8 @@ const dictionary: RawTranslationPackage = { ROUNDUP: 'RUND.OP', ROWS: 'RÆKKER', SEARCH: 'SØG', + SHEETS: 'ARK.FLERE', + SHEET: 'ARK', SIN: 'SIN', SPLIT: 'SPLIT', SQRT: 'KVROD', diff --git a/src/i18n/languages/deDE.ts b/src/i18n/languages/deDE.ts index 119d75150a..3de8ef765d 100644 --- a/src/i18n/languages/deDE.ts +++ b/src/i18n/languages/deDE.ts @@ -77,13 +77,18 @@ const dictionary: RawTranslationPackage = { INDEX: 'INDEX', INT: 'GANZZAHL', IPMT: 'ZINSZ', + ISBINARY: 'ISBINARY', ISBLANK: 'ISTLEER', + ISERR: 'ISTFEHL', ISERROR: 'ISTFEHLER', ISEVEN: 'ISTGERADE', + ISFORMULA: 'ISTFORMEL', ISLOGICAL: 'ISTLOG', + ISNA: 'ISTNV', ISNONTEXT: 'ISTKTEXT', ISNUMBER: 'ISTZAHL', ISODD: 'ISTUNGERADE', + ISREF: 'ISTBEZUG', ISTEXT: 'ISTTEXT', LEFT: 'LINKS', LEN: 'LÄNGE', @@ -101,6 +106,7 @@ const dictionary: RawTranslationPackage = { MMULT: 'MMULT', MOD: 'REST', MONTH: 'MONAT', + NA: 'NV', NOT: 'NICHT', ODD: 'UNGERADE', OFFSET: 'BEREICH.VERSCHIEBEN', @@ -119,6 +125,8 @@ const dictionary: RawTranslationPackage = { ROUNDUP: 'AUFRUNDEN', ROWS: 'ZEILEN', SEARCH: 'SUCHEN', + SHEETS: 'BLÄTTER', + SHEET: 'BLATT', SIN: 'SIN', SPLIT: 'SPLIT', SQRT: 'WURZEL', diff --git a/src/i18n/languages/enGB.ts b/src/i18n/languages/enGB.ts index 679b95dd4d..4765c7b808 100644 --- a/src/i18n/languages/enGB.ts +++ b/src/i18n/languages/enGB.ts @@ -77,13 +77,18 @@ const dictionary: RawTranslationPackage = { INDEX: 'INDEX', INT: 'INT', IPMT: 'IPMT', + ISBINARY: 'ISBINARY', ISBLANK: 'ISBLANK', + ISERR: 'ISERR', ISERROR: 'ISERROR', ISEVEN: 'ISEVEN', + ISFORMULA: 'ISFORMULA', ISLOGICAL: 'ISLOGICAL', + ISNA: 'ISNA', ISNONTEXT: 'ISNONTEXT', ISNUMBER: 'ISNUMBER', ISODD: 'ISODD', + ISREF: 'ISREF', ISTEXT: 'ISTEXT', LEFT: 'LEFT', LEN: 'LEN', @@ -101,6 +106,7 @@ const dictionary: RawTranslationPackage = { MMULT: 'MMULT', MOD: 'MOD', MONTH: 'MONTH', + NA: 'NA', NOT: 'NOT', ODD: 'ODD', OFFSET: 'OFFSET', @@ -119,6 +125,8 @@ const dictionary: RawTranslationPackage = { ROUNDUP: 'ROUNDUP', ROWS: 'ROWS', SEARCH: 'SEARCH', + SHEETS: 'SHEETS', + SHEET: 'SHEET', SIN: 'SIN', SPLIT: 'SPLIT', SQRT: 'SQRT', diff --git a/src/i18n/languages/esES.ts b/src/i18n/languages/esES.ts index e553ee2ad1..0f48e7b96e 100644 --- a/src/i18n/languages/esES.ts +++ b/src/i18n/languages/esES.ts @@ -77,13 +77,18 @@ export const dictionary: RawTranslationPackage = { INDEX: 'INDICE', INT: 'ENTERO', IPMT: 'PAGOINT', + ISBINARY: 'ISBINARY', ISBLANK: 'ESBLANCO', + ISERR: 'ESERR', ISERROR: 'ESERROR', ISEVEN: 'ES.PAR', + ISFORMULA: 'ISFORMULA', ISLOGICAL: 'ESLOGICO', + ISNA: 'ESNOD', ISNONTEXT: 'ESNOTEXTO', ISNUMBER: 'ESNUMERO', ISODD: 'ES.IMPAR', + ISREF: 'ESREF', ISTEXT: 'ESTEXTO', LEFT: 'IZQUIERDA', LEN: 'LARGO', @@ -101,6 +106,7 @@ export const dictionary: RawTranslationPackage = { MMULT: 'MMULT', MOD: 'RESIDUO', MONTH: 'MES', + NA: 'NOD', NOT: 'NO', ODD: 'REDONDEA.IMPAR', OFFSET: 'DESREF', @@ -119,6 +125,8 @@ export const dictionary: RawTranslationPackage = { ROUNDUP: 'REDONDEAR.MAS', ROWS: 'FILAS', SEARCH: 'HALLAR', + SHEETS: 'HOJAS', + SHEET: 'HOJA', SIN: 'SENO', SPLIT: 'SPLIT', SQRT: 'RAIZ', diff --git a/src/i18n/languages/fiFI.ts b/src/i18n/languages/fiFI.ts index 2d1afb7f55..dcb54d139a 100644 --- a/src/i18n/languages/fiFI.ts +++ b/src/i18n/languages/fiFI.ts @@ -77,13 +77,18 @@ const dictionary: RawTranslationPackage = { INDEX: 'INDEKSI', INT: 'KOKONAISLUKU', IPMT: 'IPMT', + ISBINARY: 'ISBINARY', ISBLANK: 'ONTYHJÄ', + ISERR: 'ONVIRH', ISERROR: 'ONVIRHE', ISEVEN: 'ONPARILLINEN', + ISFORMULA: 'ONKAAVA', ISLOGICAL: 'ONTOTUUS', + ISNA: 'ONPUUTTUU', ISNONTEXT: 'ONEI_TEKSTI', ISNUMBER: 'ONLUKU', ISODD: 'ONPARITON', + ISREF: 'ONVIITT', ISTEXT: 'ONTEKSTI', LEFT: 'VASEN', LEN: 'PITUUS', @@ -101,6 +106,7 @@ const dictionary: RawTranslationPackage = { MMULT: 'MKERRO', MOD: 'JAKOJ', MONTH: 'KUUKAUSI', + NA: 'PUUTTUU', NOT: 'EI', ODD: 'PARITON', OFFSET: 'SIIRTYMÄ', @@ -119,6 +125,8 @@ const dictionary: RawTranslationPackage = { ROUNDUP: 'PYÖRISTÄ.DES.YLÖS', ROWS: 'RIVIT', SEARCH: 'KÄY.LÄPI', + SHEETS: 'TAULUKOT', + SHEET: 'TAULUKKO', SIN: 'SIN', SPLIT: 'SPLIT', SQRT: 'NELIÖJUURI', diff --git a/src/i18n/languages/frFR.ts b/src/i18n/languages/frFR.ts index 7cac6074e3..7993c4a2e4 100644 --- a/src/i18n/languages/frFR.ts +++ b/src/i18n/languages/frFR.ts @@ -77,13 +77,18 @@ const dictionary: RawTranslationPackage = { INDEX: 'INDEX', INT: 'ENT', IPMT: 'INTPER', + ISBINARY: 'ISBINARY', ISBLANK: 'ESTVIDE', + ISERR: 'ESTERR', ISERROR: 'ESTERREUR', ISEVEN: 'EST.PAIR', + ISFORMULA: 'ESTFORMULE', ISLOGICAL: 'ESTLOGIQUE', + ISNA: 'ESTNA', ISNONTEXT: 'ESTNONTEXTE', ISNUMBER: 'ESTNUM', ISODD: 'EST.IMPAIR', + ISREF: 'ESTREF', ISTEXT: 'ESTTEXTE', LEFT: 'GAUCHE', LEN: 'NBCAR', @@ -101,6 +106,7 @@ const dictionary: RawTranslationPackage = { MMULT: 'PRODUITMAT', MOD: 'MOD', MONTH: 'MOIS', + NA: 'NA', NOT: 'NON', ODD: 'IMPAIR', OFFSET: 'DECALER', @@ -119,6 +125,8 @@ const dictionary: RawTranslationPackage = { ROUNDUP: 'ARRONDI.SUP', ROWS: 'LIGNES', SEARCH: 'CHERCHE', + SHEETS: 'FEUILLES', + SHEET: 'FEUILLE', SIN: 'SIN', SPLIT: 'SPLIT', SQRT: 'RACINE', diff --git a/src/i18n/languages/huHU.ts b/src/i18n/languages/huHU.ts index 2e5b30ec7e..9cac059907 100644 --- a/src/i18n/languages/huHU.ts +++ b/src/i18n/languages/huHU.ts @@ -77,13 +77,18 @@ const dictionary: RawTranslationPackage = { INDEX: 'INDEX', INT: 'INT', IPMT: 'RRÉSZLET', + ISBINARY: 'ISBINARY', ISBLANK: 'ÜRES', + ISERR: 'HIBA.E', ISERROR: 'HIBÁS', ISEVEN: 'PÁROSE', + ISFORMULA: 'KÉPLET', ISLOGICAL: 'LOGIKAI', + ISNA: 'NINCS', ISNONTEXT: 'NEM.SZÖVEG', ISNUMBER: 'SZÁM', ISODD: 'PÁRATLANE', + ISREF: 'HIVATKOZÁS', ISTEXT: 'SZÖVEG.E', LEFT: 'BAL', LEN: 'HOSSZ', @@ -101,6 +106,7 @@ const dictionary: RawTranslationPackage = { MMULT: 'MSZORZAT', MOD: 'MARADÉK', MONTH: 'HÓNAP', + NA: 'HIÁNYZIK', NOT: 'NEM', ODD: 'PÁRATLAN', OFFSET: 'ELTOLÁS', @@ -119,6 +125,8 @@ const dictionary: RawTranslationPackage = { ROUNDUP: 'KEREK.FEL', ROWS: 'SOROK', SEARCH: 'SZÖVEG.KERES', + SHEETS: 'LAPOK', + SHEET: 'LAP', SIN: 'SIN', SPLIT: 'SPLIT', SQRT: 'GYÖK', diff --git a/src/i18n/languages/itIT.ts b/src/i18n/languages/itIT.ts index da3dca2863..c5b3581b66 100644 --- a/src/i18n/languages/itIT.ts +++ b/src/i18n/languages/itIT.ts @@ -77,13 +77,18 @@ const dictionary: RawTranslationPackage = { INDEX: 'INDICE', INT: 'INT', IPMT: 'INTERESSI', + ISBINARY: 'ISBINARY', ISBLANK: 'VAL.VUOTO', + ISERR: 'VAL.ERR', ISERROR: 'VAL.ERRORE', ISEVEN: 'VAL.PARI', + ISFORMULA: 'VAL.FORMULA', ISLOGICAL: 'VAL.LOGICO', + ISNA: 'VAL.NON.DISP', ISNONTEXT: 'VAL.NON.TESTO', ISNUMBER: 'VAL.NUMERO', ISODD: 'VAL.DISPARI', + ISREF: 'VAL.RIF', ISTEXT: 'VAL.TESTO', LEFT: 'SINISTRA', LEN: 'LUNGHEZZA', @@ -101,6 +106,7 @@ const dictionary: RawTranslationPackage = { MMULT: 'MATR.PRODOTTO', MOD: 'RESTO', MONTH: 'MESE', + NA: 'NON.DISP', NOT: 'NON', ODD: 'DISPARI', OFFSET: 'SCARTO', @@ -119,6 +125,8 @@ const dictionary: RawTranslationPackage = { ROUNDUP: 'ARROTONDA.PER.ECC', ROWS: 'RIGHE', SEARCH: 'RICERCA', + SHEETS: 'FOGLI', + SHEET: 'FOGLIO', SIN: 'SEN', SPLIT: 'SPLIT', SQRT: 'RADQ', diff --git a/src/i18n/languages/nbNO.ts b/src/i18n/languages/nbNO.ts index 265bc3f1d6..17def8c862 100644 --- a/src/i18n/languages/nbNO.ts +++ b/src/i18n/languages/nbNO.ts @@ -77,13 +77,18 @@ const dictionary: RawTranslationPackage = { INDEX: 'INDEKS', INT: 'HELTALL', IPMT: 'RAVDRAG', + ISBINARY: 'ISBINARY', ISBLANK: 'ERTOM', + ISERR: 'ERF', ISERROR: 'ERFEIL', ISEVEN: 'ERPARTALL', + ISFORMULA: 'ERFORMEL', ISLOGICAL: 'ERLOGISK', + ISNA: 'ERIT', ISNONTEXT: 'ERIKKETEKST', ISNUMBER: 'ERTALL', ISODD: 'ERODDE', + ISREF: 'ERREF', ISTEXT: 'ERTEKST', LEFT: 'VENSTRE', LEN: 'LENGDE', @@ -101,6 +106,7 @@ const dictionary: RawTranslationPackage = { MMULT: 'MMULT', MOD: 'REST', MONTH: 'MÅNED', + NA: 'IT', NOT: 'IKKE', ODD: 'AVRUND.TIL.ODDETALL', OFFSET: 'FORSKYVNING', @@ -119,6 +125,8 @@ const dictionary: RawTranslationPackage = { ROUNDUP: 'AVRUND.OPP', ROWS: 'RADER', SEARCH: 'SØK', + SHEETS: 'SHEETS', + SHEET: 'ARK', SIN: 'SIN', SPLIT: 'SPLIT', SQRT: 'ROT', diff --git a/src/i18n/languages/nlNL.ts b/src/i18n/languages/nlNL.ts index cd9d5f6652..4011ec59a3 100644 --- a/src/i18n/languages/nlNL.ts +++ b/src/i18n/languages/nlNL.ts @@ -77,13 +77,18 @@ const dictionary: RawTranslationPackage = { INDEX: 'INDEX', INT: 'INTEGER', IPMT: 'IBET', + ISBINARY: 'ISBINARY', ISBLANK: 'ISLEEG', + ISERR: 'ISFOUT2', ISERROR: 'ISFOUT', ISEVEN: 'IS.EVEN', + ISFORMULA: 'ISFORMULE', ISLOGICAL: 'ISLOGISCH', + ISNA: 'ISNB', ISNONTEXT: 'ISGEENTEKST', ISNUMBER: 'ISGETAL', ISODD: 'IS.ONEVEN', + ISREF: 'ISVERWIJZING', ISTEXT: 'ISTEKST', LEFT: 'LINKS', LEN: 'PITUUS', @@ -101,6 +106,7 @@ const dictionary: RawTranslationPackage = { MMULT: 'PRODUCTMAT', MOD: 'REST', MONTH: 'MAAND', + NA: 'NB', NOT: 'NIET', ODD: 'ONEVEN', OFFSET: 'VERSCHUIVING', @@ -119,6 +125,8 @@ const dictionary: RawTranslationPackage = { ROUNDUP: 'AFRONDEN.NAAR.BOVEN', ROWS: 'RIJEN', SEARCH: 'VIND.SPEC', + SHEETS: 'BLADEN', + SHEET: 'BLAD', SIN: 'SIN', SPLIT: 'SPLIT', SQRT: 'WORTEL', diff --git a/src/i18n/languages/plPL.ts b/src/i18n/languages/plPL.ts index 107168e1b0..ca2620e801 100644 --- a/src/i18n/languages/plPL.ts +++ b/src/i18n/languages/plPL.ts @@ -77,13 +77,18 @@ const dictionary: RawTranslationPackage = { INDEX: 'INDEKS', INT: 'ZAOKR.DO.CAŁK', IPMT: 'IPMT', + ISBINARY: 'ISBINARY', ISBLANK: 'CZY.PUSTA', + ISERR: 'CZY.BŁ', ISERROR: 'CZY.BŁĄD', ISEVEN: 'CZY.PARZYSTE', + ISFORMULA: 'CZY.FORMUŁA', ISLOGICAL: 'CZY.LOGICZNA', + ISNA: 'CZY.BRAK', ISNONTEXT: 'CZY.NIE.TEKST', ISNUMBER: 'CZY.LICZBA', ISODD: 'CZY.NIEPARZYSTE', + ISREF: 'CZY.ADR', ISTEXT: 'CZY.TEKST', LEFT: 'LEWY', LEN: 'DŁ', @@ -101,6 +106,7 @@ const dictionary: RawTranslationPackage = { MMULT: 'MACIERZ.ILOCZYN', MOD: 'MOD', MONTH: 'MIESIĄC', + NA: 'BRAK', NOT: 'NIE', ODD: 'ZAOKR.DO.NPARZ', OFFSET: 'PRZESUNIĘCIE', @@ -119,6 +125,8 @@ const dictionary: RawTranslationPackage = { ROUNDUP: 'ZAOKR.GÓRA', ROWS: 'ILE.WIERSZY', SEARCH: 'SZUKAJ.TEKST', + SHEETS: 'ARKUSZE', + SHEET: 'ARKUSZ', SIN: 'SIN', SPLIT: 'PODZIEL.TEKST', SQRT: 'PIERWIASTEK', diff --git a/src/i18n/languages/ptPT.ts b/src/i18n/languages/ptPT.ts index cfa0ca781f..14bcf08282 100644 --- a/src/i18n/languages/ptPT.ts +++ b/src/i18n/languages/ptPT.ts @@ -77,13 +77,18 @@ const dictionary: RawTranslationPackage = { INDEX: 'ÍNDICE', INT: 'INT', IPMT: 'IPGTO', + ISBINARY: 'ISBINARY', ISBLANK: 'ÉCÉL.VAZIA', + ISERR: 'ÉERRO', ISERROR: 'ÉERROS', ISEVEN: 'ÉPAR', + ISFORMULA: 'ÉFÓRMULA', ISLOGICAL: 'ÉLÓGICO', + ISNA: 'É.NÃO.DISP', ISNONTEXT: 'É.NÃO.TEXTO', ISNUMBER: 'ÉNÚM', ISODD: 'ÉIMPAR', + ISREF: 'ÉREF', ISTEXT: 'ÉTEXTO', LEFT: 'ESQUERDA', LEN: 'NÚM.CARACT', @@ -101,6 +106,7 @@ const dictionary: RawTranslationPackage = { MMULT: 'MATRIZ.MULT', MOD: 'MOD', MONTH: 'MÊS', + NA: 'NÃO.DISP', NOT: 'NÃO', ODD: 'ÍMPAR', OFFSET: 'DESLOC', @@ -119,6 +125,8 @@ const dictionary: RawTranslationPackage = { ROUNDUP: 'ARREDONDAR.PARA.CIMA', ROWS: 'LINS', SEARCH: 'LOCALIZAR', + SHEETS: 'PLANILHAS', + SHEET: 'PLANILHA', SIN: 'SEN', SPLIT: 'SPLIT', SQRT: 'RAIZ', diff --git a/src/i18n/languages/ruRU.ts b/src/i18n/languages/ruRU.ts index 41a3b317ec..9fc40077f7 100644 --- a/src/i18n/languages/ruRU.ts +++ b/src/i18n/languages/ruRU.ts @@ -77,13 +77,18 @@ const dictionary: RawTranslationPackage = { INDEX: 'ИНДЕКС', INT: 'ЦЕЛОЕ', IPMT: 'ПРПЛТ', + ISBINARY: 'ISBINARY', ISBLANK: 'ЕПУСТО', + ISERR: 'ЕОШ', ISERROR: 'ЕОШИБКА', ISEVEN: 'ЕЧЁТН', + ISFORMULA: 'ЕФОРМУЛА', ISLOGICAL: 'ЕЛОГИЧ', + ISNA: 'ЕНД', ISNONTEXT: 'ЕНЕТЕКСТ', ISNUMBER: 'ЕЧИСЛО', ISODD: 'ЕНЕЧЁТ', + ISREF: 'ЕССЫЛКА', ISTEXT: 'ЕТЕКСТ', LEFT: 'ЛЕВСИМВ', LEN: 'ДЛСТР', @@ -101,6 +106,7 @@ const dictionary: RawTranslationPackage = { MMULT: 'МУМНОЖ', MOD: 'ОСТАТ', MONTH: 'МЕСЯЦ', + NA: 'НД', NOT: 'НЕ', ODD: 'НЕЧЁТ', OFFSET: 'СМЕЩ', @@ -119,6 +125,8 @@ const dictionary: RawTranslationPackage = { ROUNDUP: 'ОКРУГЛВВЕРХ', ROWS: 'ЧСТРОК', SEARCH: 'ПОИСК', + SHEETS: 'ЛИСТЫ', + SHEET: 'ЛИСТ', SIN: 'SIN', SPLIT: 'SPLIT', SQRT: 'КОРЕНЬ', diff --git a/src/i18n/languages/svSE.ts b/src/i18n/languages/svSE.ts index ea1b4f1e17..0772955356 100644 --- a/src/i18n/languages/svSE.ts +++ b/src/i18n/languages/svSE.ts @@ -77,13 +77,18 @@ const dictionary: RawTranslationPackage = { INDEX: 'INDEX', INT: 'HELTAL', IPMT: 'RBETALNING', + ISBINARY: 'ISBINARY', ISBLANK: 'ÄRTOM', + ISERR: 'ÄRF', ISERROR: 'ÄRFEL', ISEVEN: 'ÄRJÄMN', + ISFORMULA: 'ISFORMEL', ISLOGICAL: 'ÄRLOGISK', + ISNA: 'ÄRSAKNAD', ISNONTEXT: 'ÄREJTEXT', ISNUMBER: 'ÄRTAL', ISODD: 'ÄRUDDA', + ISREF: 'ÄRREF', ISTEXT: 'ÄRTEXT', LEFT: 'VÄNSTER', LEN: 'LÄNGD', @@ -101,6 +106,7 @@ const dictionary: RawTranslationPackage = { MMULT: 'MMULT', MOD: 'REST', MONTH: 'MÅNAD', + NA: 'SAKNAS', NOT: 'ICKE', ODD: 'UDDA', OFFSET: 'FÖRSKJUTNING', @@ -119,6 +125,8 @@ const dictionary: RawTranslationPackage = { ROUNDUP: 'AVRUNDA.UPPÅT', ROWS: 'RADER', SEARCH: 'SÖK', + SHEETS: 'SHEETS', + SHEET: 'SHEET', SIN: 'SIN', SPLIT: 'SPLIT', SQRT: 'ROT', diff --git a/src/i18n/languages/trTR.ts b/src/i18n/languages/trTR.ts index edf855d702..52ba9e478b 100644 --- a/src/i18n/languages/trTR.ts +++ b/src/i18n/languages/trTR.ts @@ -77,13 +77,18 @@ const dictionary: RawTranslationPackage = { INDEX: 'İNDİS', INT: 'TAMSAYI', IPMT: 'FAİZTUTARI', + ISBINARY: 'ISBINARY', ISBLANK: 'EBOŞSA', + ISERR: 'EHATA', ISERROR: 'EHATALIYSA', ISEVEN: 'ÇİFTMİ', + ISFORMULA: 'EFORMÜLSE', ISLOGICAL: 'EMANTIKSALSA', + ISNA: 'EYOKSA', ISNONTEXT: 'EMETİNDEĞİLSE', ISNUMBER: 'ESAYIYSA', ISODD: 'TEKMİ', + ISREF: 'EREFSE', ISTEXT: 'EMETİNSE', LEFT: 'SOL', LEN: 'UZUNLUK', @@ -101,6 +106,7 @@ const dictionary: RawTranslationPackage = { MMULT: 'DÇARP', MOD: 'MOD', MONTH: 'AY', + NA: 'YOKSAY', NOT: 'DEĞİL', ODD: 'TEK', OFFSET: 'KAYDIR', @@ -119,6 +125,8 @@ const dictionary: RawTranslationPackage = { ROUNDUP: 'YUKARIYUVARLA', ROWS: 'SATIRSAY', SEARCH: 'MBUL', + SHEETS: 'SAYFALAR', + SHEET: 'SAYFA', SIN: 'SİN', SPLIT: 'SPLIT', SQRT: 'KAREKÖK', diff --git a/src/interpreter/plugin/FormulaTextPlugin.ts b/src/interpreter/plugin/FormulaTextPlugin.ts index 3d6fe35301..23cae60840 100644 --- a/src/interpreter/plugin/FormulaTextPlugin.ts +++ b/src/interpreter/plugin/FormulaTextPlugin.ts @@ -3,49 +3,36 @@ * Copyright (c) 2020 Handsoncode. All rights reserved. */ -import {AstNodeType, ProcedureAst} from '../../parser' +import {ProcedureAst} from '../../parser' import {CellError, ErrorType, InternalScalarValue, SimpleCellAddress} from '../../Cell' import {FunctionPlugin} from '../index' -import {AbsoluteCellRange} from '../../AbsoluteCellRange' -import {Maybe} from '../../Maybe' +import {ArgumentTypes} from './FunctionPlugin' export class FormulaTextPlugin extends FunctionPlugin { public static implementedFunctions = { 'FORMULATEXT': { method: 'formulatext', + parameters: [ + {argumentType: ArgumentTypes.NOERROR} + ], doesNotNeedArgumentsToBeComputed: true, isDependentOnSheetStructureChange: true }, } + /** + * Corresponds to FORMULATEXT(value) + * + * Returns a formula in a given cell as a string. + * + * @param ast + * @param formulaAddress + */ public formulatext(ast: ProcedureAst, formulaAddress: SimpleCellAddress): InternalScalarValue { - if (ast.args.length !== 1) { - return new CellError(ErrorType.NA) - } - - const arg = ast.args[0] - - let cellReference: Maybe - - if (arg.type === AstNodeType.CELL_REFERENCE) { - cellReference = arg.reference.toSimpleCellAddress(formulaAddress) - } else if (arg.type === AstNodeType.CELL_RANGE || arg.type === AstNodeType.COLUMN_RANGE || arg.type === AstNodeType.ROW_RANGE) { - try { - cellReference = AbsoluteCellRange.fromAst(arg, formulaAddress).start - } catch (e) { - return new CellError(ErrorType.REF) - } - } - - if (cellReference === undefined) { - const value = this.evaluateAst(arg, formulaAddress) - if (value instanceof CellError) { - return value - } - return new CellError(ErrorType.NA) - } - - const formula = this.serialization.getCellFormula(cellReference) - return formula || new CellError(ErrorType.NA) + return this.runFunctionWithReferenceArgument(ast.args, formulaAddress, this.metadata('FORMULATEXT'), + () => new CellError(ErrorType.NA), + (cellReference: SimpleCellAddress) => this.serialization.getCellFormula(cellReference) || new CellError(ErrorType.NA), + () => new CellError(ErrorType.NA) + ) } -} \ No newline at end of file +} diff --git a/src/interpreter/plugin/FunctionPlugin.ts b/src/interpreter/plugin/FunctionPlugin.ts index 612aa8c4ba..db5e467801 100644 --- a/src/interpreter/plugin/FunctionPlugin.ts +++ b/src/interpreter/plugin/FunctionPlugin.ts @@ -9,7 +9,7 @@ import {ColumnSearchStrategy} from '../../ColumnSearch/ColumnSearchStrategy' import {Config} from '../../Config' import {DependencyGraph} from '../../DependencyGraph' import {Maybe} from '../../Maybe' -import {Ast, ProcedureAst} from '../../parser' +import {Ast, AstNodeType, ProcedureAst} from '../../parser' import {coerceScalarToBoolean, coerceScalarToString} from '../ArithmeticHelper' import {Interpreter} from '../Interpreter' import {InterpreterValue, SimpleRangeValue} from '../InterpreterValue' @@ -19,7 +19,20 @@ export interface ImplementedFunctions { [formulaId: string]: FunctionMetadata, } -export interface FunctionMetadata { +export interface FunctionArguments { + parameters?: FunctionArgument[], + /** + * Used for functions with variable number of arguments -- last defined argument is repeated indefinitely. + */ + repeatLastArg?: boolean, + + /** + * Ranges in arguments are inlined to (possibly multiple) scalar arguments. + */ + expandRanges?: boolean, +} + +export interface FunctionMetadata extends FunctionArguments{ method: string, parameters?: FunctionArgument[], isVolatile?: boolean, @@ -222,7 +235,7 @@ export abstract class FunctionPlugin { protected runFunction = ( args: Ast[], formulaAddress: SimpleCellAddress, - functionDefinition: FunctionMetadata, + functionDefinition: FunctionArguments, fn: (...arg: any) => InternalScalarValue ) => { const argumentDefinitions: FunctionArgument[] = functionDefinition.parameters! @@ -272,6 +285,40 @@ export abstract class FunctionPlugin { return argCoerceFailure ?? fn(...coercedArguments) } + protected runFunctionWithReferenceArgument = ( + args: Ast[], + formulaAddress: SimpleCellAddress, + argumentDefinitions: FunctionArguments, + noArgCallback: () => InternalScalarValue, + referenceCallback: (reference: SimpleCellAddress) => InternalScalarValue, + nonReferenceCallback: (...arg: any) => InternalScalarValue + ) => { + if (args.length === 0) { + return noArgCallback() + } else if (args.length > 1) { + return new CellError(ErrorType.NA) + } + const arg = args[0] + + let cellReference: Maybe + + if (arg.type === AstNodeType.CELL_REFERENCE) { + cellReference = arg.reference.toSimpleCellAddress(formulaAddress) + } else if (arg.type === AstNodeType.CELL_RANGE || arg.type === AstNodeType.COLUMN_RANGE || arg.type === AstNodeType.ROW_RANGE) { + try { + cellReference = AbsoluteCellRange.fromAst(arg, formulaAddress).start + } catch (e) { + return new CellError(ErrorType.REF) + } + } + + if (cellReference !== undefined) { + return referenceCallback(cellReference) + } + + return this.runFunction(args, formulaAddress, argumentDefinitions, nonReferenceCallback) + } + protected metadata(name: string): FunctionMetadata { const params = (this.constructor as FunctionPluginDefinition).implementedFunctions[name] if (params !== undefined) { diff --git a/src/interpreter/plugin/InformationPlugin.ts b/src/interpreter/plugin/InformationPlugin.ts index 30ea9e1b10..cba11aeeb4 100644 --- a/src/interpreter/plugin/InformationPlugin.ts +++ b/src/interpreter/plugin/InformationPlugin.ts @@ -5,6 +5,7 @@ import {AbsoluteCellRange} from '../../AbsoluteCellRange' import {CellError, EmptyValue, ErrorType, InternalScalarValue, SimpleCellAddress} from '../../Cell' +import {FormulaCellVertex, MatrixVertex} from '../../DependencyGraph' import {AstNodeType, ProcedureAst} from '../../parser' import {InterpreterValue} from '../InterpreterValue' import {ArgumentTypes, FunctionPlugin} from './FunctionPlugin' @@ -14,6 +15,42 @@ import {ArgumentTypes, FunctionPlugin} from './FunctionPlugin' */ export class InformationPlugin extends FunctionPlugin { public static implementedFunctions = { + 'COLUMNS': { + method: 'columns', + isDependentOnSheetStructureChange: true, + doesNotNeedArgumentsToBeComputed: true, + }, + 'ISBINARY': { + method: 'isbinary', + parameters: [ + {argumentType: ArgumentTypes.STRING} + ] + }, + 'ISERR': { + method: 'iserr', + parameters: [ + {argumentType: ArgumentTypes.SCALAR} + ] + }, + 'ISFORMULA': { + method: 'isformula', + parameters: [ + {argumentType: ArgumentTypes.NOERROR} + ], + doesNotNeedArgumentsToBeComputed: true + }, + 'ISNA': { + method: 'isna', + parameters: [ + {argumentType: ArgumentTypes.SCALAR} + ] + }, + 'ISREF': { + method: 'isref', + parameters:[ + {argumentType: ArgumentTypes.SCALAR} + ] + }, 'ISERROR': { method: 'iserror', parameters: [ @@ -50,19 +87,59 @@ export class InformationPlugin extends FunctionPlugin { { argumentType: ArgumentTypes.SCALAR} ] }, - 'COLUMNS': { - method: 'columns', - isDependentOnSheetStructureChange: true, - doesNotNeedArgumentsToBeComputed: true, + 'INDEX': { + method: 'index', + }, + 'NA': { + method: 'na' }, 'ROWS': { method: 'rows', isDependentOnSheetStructureChange: true, doesNotNeedArgumentsToBeComputed: true, }, - 'INDEX': { - method: 'index', + 'SHEET': { + method: 'sheet', + parameters: [ + {argumentType: ArgumentTypes.NOERROR} + ], + doesNotNeedArgumentsToBeComputed: true }, + 'SHEETS': { + method: 'sheets', + parameters: [ + {argumentType: ArgumentTypes.NOERROR} + ], + doesNotNeedArgumentsToBeComputed: true + } + } + + /** + * Corresponds to ISBINARY(value) + * + * Returns true if provided value is a valid binary number + * + * @param ast + * @param formulaAddress + */ + public isbinary(ast: ProcedureAst, formulaAddress: SimpleCellAddress): InternalScalarValue { + return this.runFunction(ast.args, formulaAddress, this.metadata('ISBINARY'), (arg: string) => + /^[01]{1,10}$/.test(arg) + ) + } + + /** + * Corresponds to ISERR(value) + * + * Returns true if provided value is an error except #N/A! + * + * @param ast + * @param formulaAddress + */ + public iserr(ast: ProcedureAst, formulaAddress: SimpleCellAddress): InternalScalarValue { + return this.runFunction(ast.args, formulaAddress, this.metadata('ISERR'), (arg: InternalScalarValue) => + (arg instanceof CellError && arg.type !== ErrorType.NA) + ) } /** @@ -79,6 +156,25 @@ export class InformationPlugin extends FunctionPlugin { ) } + /** + * Corresponds to ISFORMULA(value) + * + * Checks whether referenced cell is a formula + * + * @param ast + * @param formulaAddress + */ + public isformula(ast: ProcedureAst, formulaAddress: SimpleCellAddress): InternalScalarValue { + return this.runFunctionWithReferenceArgument(ast.args, formulaAddress, this.metadata('ISFORMULA'), + () => new CellError(ErrorType.NA), + (reference: SimpleCellAddress) => { + const vertex = this.dependencyGraph.addressMapping.getCell(reference) + return vertex instanceof FormulaCellVertex || (vertex instanceof MatrixVertex && vertex.isFormula()) + }, + () => new CellError(ErrorType.NA) + ) + } + /** * Corresponds to ISBLANK(value) * @@ -93,6 +189,20 @@ export class InformationPlugin extends FunctionPlugin { ) } + /** + * Corresponds to ISNA(value) + * + * Returns true if provided value is #N/A! error + * + * @param ast + * @param formulaAddress + */ + public isna(ast: ProcedureAst, formulaAddress: SimpleCellAddress): InternalScalarValue { + return this.runFunction(ast.args, formulaAddress, this.metadata('ISNA'), (arg: InternalScalarValue) => + (arg instanceof CellError && arg.type == ErrorType.NA) + ) + } + /** * Corresponds to ISNUMBER(value) * @@ -121,6 +231,21 @@ export class InformationPlugin extends FunctionPlugin { ) } + /** + * Corresponds to ISREF(value) + * + * Returns true if provided value is #REF! error + * + * @param ast + * @param formulaAddress + */ + public isref(ast: ProcedureAst, formulaAddress: SimpleCellAddress): InternalScalarValue { + return this.runFunction(ast.args, formulaAddress, this.metadata('ISREF'), (arg: InternalScalarValue) => + (arg instanceof CellError && (arg.type == ErrorType.REF || arg.type == ErrorType.CYCLE)) + ) + } + + /** * Corresponds to ISTEXT(value) * @@ -235,4 +360,56 @@ export class InformationPlugin extends FunctionPlugin { const address = range.getAddress(columnValue - 1, rowValue - 1) return this.dependencyGraph.getCellValue(address) } + + /** + * Corresponds to NA() + * + * Returns #N/A! + * + * @param _ast + * @param _formulaAddress + */ + public na(_ast: ProcedureAst, _formulaAddress: SimpleCellAddress): CellError { + return new CellError(ErrorType.NA) + } + + /** + * Corresponds to SHEET(value) + * + * Returns sheet number of a given value or a formula sheet number if no argument is provided + * + * @param ast + * @param formulaAddress + * */ + public sheet(ast: ProcedureAst, formulaAddress: SimpleCellAddress): InternalScalarValue { + return this.runFunctionWithReferenceArgument(ast.args, formulaAddress, {parameters: [{argumentType: ArgumentTypes.STRING}]}, + () => formulaAddress.sheet + 1, + (reference: SimpleCellAddress) => reference.sheet + 1, + (value: string) => { + const sheetNumber = this.dependencyGraph.sheetMapping.get(value) + if (sheetNumber !== undefined) { + return sheetNumber + 1 + } else { + return new CellError(ErrorType.NA) + } + } + ) + } + + /** + * Corresponds to SHEETS(value) + * + * Returns number of sheet of a given reference or number of all sheets in workbook when no argument is provided. + * It returns always 1 for a valid reference as 3D references are not supported. + * + * @param ast + * @param formulaAddress + * */ + public sheets(ast: ProcedureAst, formulaAddress: SimpleCellAddress): InternalScalarValue { + return this.runFunctionWithReferenceArgument(ast.args, formulaAddress, {parameters: [{argumentType: ArgumentTypes.STRING}]}, + () => this.dependencyGraph.sheetMapping.numberOfSheets(), // return number of sheets if no argument + () => 1, // return 1 for valid reference + () => new CellError(ErrorType.VALUE) // error otherwise + ) + } } diff --git a/src/interpreter/plugin/index.ts b/src/interpreter/plugin/index.ts index 6d84e997a1..b26299efb9 100644 --- a/src/interpreter/plugin/index.ts +++ b/src/interpreter/plugin/index.ts @@ -37,4 +37,4 @@ export {CharPlugin} from './CharPlugin' export {CodePlugin} from './CodePlugin' export {ErrorFunctionPlugin} from './ErrorFunctionPlugin' export {CorrelPlugin} from './CorrelPlugin' -export {FormulaTextPlugin} from './FormulaTextPlugin' \ No newline at end of file +export {FormulaTextPlugin} from './FormulaTextPlugin' diff --git a/test/interpreter/function-isbinary.spec.ts b/test/interpreter/function-isbinary.spec.ts new file mode 100644 index 0000000000..178fd5d2c8 --- /dev/null +++ b/test/interpreter/function-isbinary.spec.ts @@ -0,0 +1,24 @@ +import {HyperFormula} from '../../src' +import {adr} from '../testUtils' + +describe('Function ISBINARY', () => { + it('should return true for binary numbers', () => { + const engine = HyperFormula.buildFromArray([ + ['=ISBINARY("1010")', '=ISBINARY(1001)', '=ISBINARY(010)'] + ]) + + expect(engine.getCellValue(adr('A1'))).toEqual(true) + expect(engine.getCellValue(adr('B1'))).toEqual(true) + expect(engine.getCellValue(adr('C1'))).toEqual(true) + }) + + it('should return false otherwise', () => { + const engine = HyperFormula.buildFromArray([ + ['=ISBINARY("foo")', '=ISBINARY(123)', '=ISBINARY(TRUE())'] + ]) + + expect(engine.getCellValue(adr('A1'))).toEqual(false) + expect(engine.getCellValue(adr('B1'))).toEqual(false) + expect(engine.getCellValue(adr('C1'))).toEqual(false) + }) +}) \ No newline at end of file diff --git a/test/interpreter/function-iserr.spec.ts b/test/interpreter/function-iserr.spec.ts new file mode 100644 index 0000000000..b678e8eec4 --- /dev/null +++ b/test/interpreter/function-iserr.spec.ts @@ -0,0 +1,52 @@ +import {HyperFormula} from '../../src' +import {ErrorType} from '../../src/Cell' +import {adr, detailedError} from '../testUtils' + +describe('Function ISERR', () => { + it('should return true for common errors', () => { + const engine = HyperFormula.buildFromArray([ + ['=ISERR(1/0)', '=ISERR(FOO())'], + ]) + + expect(engine.getCellValue(adr('A1'))).toEqual(true) + expect(engine.getCellValue(adr('B1'))).toEqual(true) + }) + + it('should return false for #N/A!', () => { + const engine = HyperFormula.buildFromArray([ + ['=ISERR(TRUE(1))'], + ]) + + expect(engine.getCellValue(adr('A1'))).toEqual(false) + }) + + it('should return false for valid formulas', () => { + const engine = HyperFormula.buildFromArray([ + ['=ISERR(1)', '=ISERR(TRUE())', '=ISERR("foo")', '=ISERR(ISERR(1/0))', '=ISERR(A1)'], + ]) + expect(engine.getCellValue(adr('A1'))).toEqual(false) + expect(engine.getCellValue(adr('B1'))).toEqual(false) + expect(engine.getCellValue(adr('C1'))).toEqual(false) + expect(engine.getCellValue(adr('D1'))).toEqual(false) + expect(engine.getCellValue(adr('E1'))).toEqual(false) + }) + + it('takes exactly one argument', () => { + const engine = HyperFormula.buildFromArray([ + ['=ISERR(1, 2)', '=ISERR()'], + ]) + expect(engine.getCellValue(adr('A1'))).toEqual(detailedError(ErrorType.NA)) + expect(engine.getCellValue(adr('B1'))).toEqual(detailedError(ErrorType.NA)) + }) + + // Inconsistency with Product 1 + it('range value results in VALUE error', () => { + const engine = HyperFormula.buildFromArray([ + ['=4/1'], + ['=4/0', '=ISERR(A1:A3)'], + ['=4/2'], + ]) + + expect(engine.getCellValue(adr('B2'))).toEqual(detailedError(ErrorType.VALUE)) + }) +}) diff --git a/test/interpreter/function-isformula.spec.ts b/test/interpreter/function-isformula.spec.ts new file mode 100644 index 0000000000..17730fc999 --- /dev/null +++ b/test/interpreter/function-isformula.spec.ts @@ -0,0 +1,57 @@ +import {ErrorType, HyperFormula} from '../../src' +import {adr, detailedError} from '../testUtils' + +describe('Function ISFORMULA', () => { + it('should return true for cell with formula', () => { + const engine = HyperFormula.buildFromArray([ + ['=A1', '=ISFORMULA(A1)'] + ]) + + expect(engine.getCellValue(adr('B1'))).toEqual(true) + }) + + it('should return false for cell without formula', () => { + const engine = HyperFormula.buildFromArray([ + ['foo', '=ISFORMULA(A1)', '=ISFORMULA(A2)'] + ]) + + expect(engine.getCellValue(adr('B1'))).toEqual(false) + expect(engine.getCellValue(adr('C1'))).toEqual(false) + }) + + it('should work with start of a range', () => { + const engine = HyperFormula.buildFromArray([ + ['=A1', 2, '=ISFORMULA(A1:A2)', '=ISFORMULA(B1:B2)'] + ]) + + expect(engine.getCellValue(adr('C1'))).toEqual(true) + expect(engine.getCellValue(adr('D1'))).toEqual(false) + }) + + it('should propagate error', () => { + const engine = HyperFormula.buildFromArray([ + ['=ISFORMULA(1/0)'] + ]) + + expect(engine.getCellValue(adr('A1'))).toEqual(detailedError(ErrorType.DIV_BY_ZERO)) + }) + + it('should return NA otherwise', () => { + const engine = HyperFormula.buildFromArray([ + ['=ISFORMULA()', '=ISFORMULA(A1, A2)', '=ISFORMULA("foo")', '=ISFORMULA(42)'] + ]) + + expect(engine.getCellValue(adr('A1'))).toEqual(detailedError(ErrorType.NA)) + expect(engine.getCellValue(adr('B1'))).toEqual(detailedError(ErrorType.NA)) + expect(engine.getCellValue(adr('C1'))).toEqual(detailedError(ErrorType.NA)) + expect(engine.getCellValue(adr('D1'))).toEqual(detailedError(ErrorType.NA)) + }) + + it('should work for itself', () => { + const engine = HyperFormula.buildFromArray([ + ['=ISFORMULA(A1)'] + ]) + + expect(engine.getCellValue(adr('A1'))).toEqual(true) + }) +}) \ No newline at end of file diff --git a/test/interpreter/function-isna.spec.ts b/test/interpreter/function-isna.spec.ts new file mode 100644 index 0000000000..954892e916 --- /dev/null +++ b/test/interpreter/function-isna.spec.ts @@ -0,0 +1,41 @@ +import {HyperFormula} from '../../src' +import {ErrorType} from '../../src/Cell' +import {adr, detailedError} from '../testUtils' + +describe('Function ISNA', () => { + it('should return true for #NA! error', () => { + const engine = HyperFormula.buildFromArray([ + ['=TRUE(1)', '=ISNA(A1)', '=ISNA(TRUE(1))'], + ]) + + expect(engine.getCellValue(adr('B1'))).toEqual(true) + expect(engine.getCellValue(adr('C1'))).toEqual(true) + }) + + it('should return false for other values', () => { + const engine = HyperFormula.buildFromArray([ + ['=ISNA(1)', '=ISNA(TRUE())', '=ISNA("foo")', '=ISNA(A1)'], + ]) + expect(engine.getCellValue(adr('A1'))).toEqual(false) + expect(engine.getCellValue(adr('B1'))).toEqual(false) + expect(engine.getCellValue(adr('C1'))).toEqual(false) + expect(engine.getCellValue(adr('D1'))).toEqual(false) + }) + + it('takes exactly one argument', () => { + const engine = HyperFormula.buildFromArray([ + ['=ISNA(1, 2)', '=ISNA()'], + ]) + expect(engine.getCellValue(adr('A1'))).toEqual(detailedError(ErrorType.NA)) + expect(engine.getCellValue(adr('B1'))).toEqual(detailedError(ErrorType.NA)) + }) + + // Inconsistency with Product 1 + it('range value results in VALUE error', () => { + const engine = HyperFormula.buildFromArray([ + ['=TRUE(1)'], + ['=TRUE(1)', '=ISNA(A1:A2)'], + ]) + expect(engine.getCellValue(adr('B2'))).toEqual(detailedError(ErrorType.VALUE)) + }) +}) diff --git a/test/interpreter/function-isref.spec.ts b/test/interpreter/function-isref.spec.ts new file mode 100644 index 0000000000..7005c8b0e8 --- /dev/null +++ b/test/interpreter/function-isref.spec.ts @@ -0,0 +1,59 @@ +import {HyperFormula} from '../../src' +import {ErrorType} from '../../src/Cell' +import {adr, detailedError} from '../testUtils' + +describe('Function ISREF', () => { + it('should return true for #REF!', () => { + const engine = HyperFormula.buildFromArray([ + ['=#REF!', '=ISREF(A1)'], + ]) + + expect(engine.getCellValue(adr('B1'))).toEqual(true) + }) + + it('should return true for #CYCLE!', () => { + const engine = HyperFormula.buildFromArray([ + ['=A1', '=ISREF(A1)'], + ]) + + expect(engine.getCellValue(adr('B1'))).toEqual(true) + }) + + it('should return false for other values', () => { + const engine = HyperFormula.buildFromArray([ + ['=ISREF(1)', '=ISREF(TRUE())', '=ISREF("foo")', '=ISREF(A1)'], + ]) + expect(engine.getCellValue(adr('A1'))).toEqual(false) + expect(engine.getCellValue(adr('B1'))).toEqual(false) + expect(engine.getCellValue(adr('C1'))).toEqual(false) + expect(engine.getCellValue(adr('D1'))).toEqual(false) + }) + + it('takes exactly one argument', () => { + const engine = HyperFormula.buildFromArray([ + ['=ISREF(1, 2)', '=ISREF()'], + ]) + expect(engine.getCellValue(adr('A1'))).toEqual(detailedError(ErrorType.NA)) + expect(engine.getCellValue(adr('B1'))).toEqual(detailedError(ErrorType.NA)) + }) + + // Inconsistency with Product 1 + it('range value results in VALUE error', () => { + const engine = HyperFormula.buildFromArray([ + ['=A1'], + ['=A2', '=ISREF(A1:A3)'], + ]) + + expect(engine.getCellValue(adr('B2'))).toEqual(detailedError(ErrorType.VALUE)) + }) + + // Inconsistency with Product 1 + it('returns #CYCLE! for itself', () => { + /* TODO can we handle such case correctly? */ + const engine = HyperFormula.buildFromArray([ + ['=ISREF(A1)'], + ]) + + expect(engine.getCellValue(adr('A1'))).toEqual(detailedError(ErrorType.CYCLE)) + }) +}) diff --git a/test/interpreter/function-sheet.spec.ts b/test/interpreter/function-sheet.spec.ts new file mode 100644 index 0000000000..7018a397f3 --- /dev/null +++ b/test/interpreter/function-sheet.spec.ts @@ -0,0 +1,81 @@ +import {ErrorType, HyperFormula} from '../../src' +import {adr, detailedError} from '../testUtils' + +describe('Function SHEET', () => { + it('should return formula sheet number', () => { + const engine = HyperFormula.buildFromSheets({ + 'Sheet1': [['=SHEET()']], + 'Sheet2': [['=SHEET()']], + }) + + expect(engine.getCellValue(adr('A1'))).toEqual(1) + expect(engine.getCellValue(adr('A1', 1))).toEqual(2) + }) + + it('should return reference sheet number for self sheet reference', () => { + const engine = HyperFormula.buildFromSheets({ + 'Sheet1': [['=SHEET(B1)']], + 'Sheet2': [['=SHEET(B1)', '=1/0']], + }) + + expect(engine.getCellValue(adr('A1'))).toEqual(1) + expect(engine.getCellValue(adr('A1', 1))).toEqual(2) + }) + + it('should return reference sheet number for absolute sheet reference', () => { + const engine = HyperFormula.buildFromSheets({ + 'Sheet1': [['=SHEET(Sheet1!B1)', '=SHEET(Sheet2!B1)']], + 'Sheet2': [['=SHEET(Sheet1!B1)', '=SHEET(Sheet2!B2)']], + }) + + expect(engine.getCellValue(adr('A1'))).toEqual(1) + expect(engine.getCellValue(adr('B1'))).toEqual(2) + expect(engine.getCellValue(adr('A1', 1))).toEqual(1) + expect(engine.getCellValue(adr('B1', 1))).toEqual(2) + }) + + it('should return range sheet number', () => { + const engine = HyperFormula.buildFromSheets({ + 'Sheet1': [['=SHEET(B1:B2)', '=SHEET(Sheet2!A1:B1)']], + 'Sheet2': [['=SHEET(B1:B2)', '=SHEET(Sheet1!A1:B1)']], + }) + + expect(engine.getCellValue(adr('A1'))).toEqual(1) + expect(engine.getCellValue(adr('B1'))).toEqual(2) + expect(engine.getCellValue(adr('A1', 1))).toEqual(2) + expect(engine.getCellValue(adr('B1', 1))).toEqual(1) + }) + + it('should return VALUE for non existing sheet', () => { + const engine = HyperFormula.buildFromSheets({ + 'Sheet1': [['=SHEET("FOO")', '=SHEET(1)']], + }) + + expect(engine.getCellValue(adr('A1'))).toEqual(detailedError(ErrorType.NA)) + expect(engine.getCellValue(adr('B1'))).toEqual(detailedError(ErrorType.NA)) + }) + + it('should coerce', () => { + const engine = HyperFormula.buildFromArray([]) + engine.addSheet('TRUE') + engine.addSheet('1') + + engine.setCellContents(adr('A1'), [['=SHEET(1=1)']]) + engine.setCellContents(adr('B1'), [['=SHEET(1)']]) + + expect(engine.getCellValue(adr('A1'))).toEqual(2) + expect(engine.getCellValue(adr('B1'))).toEqual(3) + }) + + it('should propagate errors', () => { + const engine = HyperFormula.buildFromArray([['=SHEET(1/0)']]) + + expect(engine.getCellValue(adr('A1'))).toEqual(detailedError(ErrorType.DIV_BY_ZERO)) + }) + + it('should work for itself', () => { + const engine = HyperFormula.buildFromArray([['=SHEET(A1)']]) + + expect(engine.getCellValue(adr('A1'))).toEqual(1) + }) +}) \ No newline at end of file diff --git a/test/interpreter/function-sheets.spec.ts b/test/interpreter/function-sheets.spec.ts new file mode 100644 index 0000000000..00e1679f4b --- /dev/null +++ b/test/interpreter/function-sheets.spec.ts @@ -0,0 +1,42 @@ +import {ErrorType, HyperFormula} from '../../src' +import {adr, detailedError} from '../testUtils' + +describe('Function SHEETS', () => { + it('should return number of sheets if no argument provided', () => { + const engine = HyperFormula.buildFromSheets({ + 'Sheet1': [['=SHEETS()']], + 'Sheet2': [], + }) + + expect(engine.getCellValue(adr('A1'))).toEqual(2) + }) + + it('should return 1 for a valid reference', () => { + const engine = HyperFormula.buildFromArray([['=SHEETS(B1)', '=SHEETS(A1:A2)', '=SHEETS(A:B)', '=SHEETS(1:2)']]) + + expect(engine.getCellValue(adr('A1'))).toEqual(1) + expect(engine.getCellValue(adr('B1'))).toEqual(1) + expect(engine.getCellValue(adr('C1'))).toEqual(1) + expect(engine.getCellValue(adr('D1'))).toEqual(1) + }) + + it('should return VALUE for non-reference parameter', () => { + const engine = HyperFormula.buildFromArray([['=SHEETS(1)', '=SHEETS("foo")', '=SHEETS(TRUE())']]) + + expect(engine.getCellValue(adr('A1'))).toEqual(detailedError(ErrorType.VALUE)) + expect(engine.getCellValue(adr('B1'))).toEqual(detailedError(ErrorType.VALUE)) + expect(engine.getCellValue(adr('C1'))).toEqual(detailedError(ErrorType.VALUE)) + }) + + it('should propagate errors', () => { + const engine = HyperFormula.buildFromArray([['=SHEETS(1/0)']]) + + expect(engine.getCellValue(adr('A1'))).toEqual(detailedError(ErrorType.DIV_BY_ZERO)) + }) + + it('should work for itself', () => { + const engine = HyperFormula.buildFromArray([['=SHEETS(A1)']]) + + expect(engine.getCellValue(adr('A1'))).toEqual(1) + }) +}) \ No newline at end of file diff --git a/test/interpreter/functions-na.spec.ts b/test/interpreter/functions-na.spec.ts new file mode 100644 index 0000000000..f175439108 --- /dev/null +++ b/test/interpreter/functions-na.spec.ts @@ -0,0 +1,13 @@ +import {ErrorType, HyperFormula} from '../../src' +import {adr, detailedError} from '../testUtils' + +describe('Function NA', () => { + it('should work', () => { + const engine = HyperFormula.buildFromArray([ + ['=NA()', '=NA(1)'], + ]) + + expect(engine.getCellValue(adr('A1'))).toEqual(detailedError(ErrorType.NA)) + expect(engine.getCellValue(adr('B1'))).toEqual(detailedError(ErrorType.NA)) + }) +}) \ No newline at end of file