diff options
author | Eike Rathke <erack@redhat.com> | 2013-01-12 17:48:59 +0100 |
---|---|---|
committer | Noel Power <noel.power@suse.com> | 2013-01-14 12:31:02 +0000 |
commit | 6ce6ddcc57b3ef7c643523e26b6caa69f5530178 (patch) | |
tree | ce182fb9c076dba8cd444ae39278813dd0800ea6 /formula | |
parent | f9112b4f3b1952d23290597963d63c92d5356352 (diff) |
fdo#56124 add functions IFERROR and IFNA to calc as in ODFF1.2
Change-Id: Ic282e1510e121be8fe52320f1f0fe0acc4b9a652
Diffstat (limited to 'formula')
-rw-r--r-- | formula/inc/formula/compiler.hrc | 42 | ||||
-rw-r--r-- | formula/inc/formula/errorcodes.hxx | 3 | ||||
-rw-r--r-- | formula/inc/formula/opcode.hxx | 2 | ||||
-rw-r--r-- | formula/source/core/api/FormulaCompiler.cxx | 79 | ||||
-rw-r--r-- | formula/source/core/api/token.cxx | 22 | ||||
-rw-r--r-- | formula/source/core/resource/core_resource.src | 12 |
6 files changed, 117 insertions, 43 deletions
diff --git a/formula/inc/formula/compiler.hrc b/formula/inc/formula/compiler.hrc index 582e3a589c93..7f976ae3a163 100644 --- a/formula/inc/formula/compiler.hrc +++ b/formula/inc/formula/compiler.hrc @@ -29,26 +29,28 @@ #define SC_OPCODE_NAME 4 #define SC_OPCODE_EXTERNAL_REF 5 #define SC_OPCODE_IF 6 /* jump commands */ -#define SC_OPCODE_CHOSE 7 -#define SC_OPCODE_OPEN 8 /* parentheses and separators */ -#define SC_OPCODE_CLOSE 9 -#define SC_OPCODE_SEP 10 -#define SC_OPCODE_MISSING 11 /* special OpCodes */ -#define SC_OPCODE_BAD 12 -#define SC_OPCODE_STRINGXML 13 -#define SC_OPCODE_SPACES 14 -#define SC_OPCODE_MAT_REF 15 -#define SC_OPCODE_DB_AREA 16 /* additional access operators */ -#define SC_OPCODE_MACRO 17 -#define SC_OPCODE_COL_ROW_NAME 18 -#define SC_OPCODE_COL_ROW_NAME_AUTO 19 -#define SC_OPCODE_PERCENT_SIGN 20 /* operator _follows_ value */ -#define SC_OPCODE_ARRAY_OPEN 21 -#define SC_OPCODE_ARRAY_CLOSE 22 -#define SC_OPCODE_ARRAY_ROW_SEP 23 -#define SC_OPCODE_ARRAY_COL_SEP 24 /* some convs use sep != col_sep */ -#define SC_OPCODE_STOP_DIV 25 -#define SC_OPCODE_SKIP 26 /* used to skip raw tokens during string compilation */ +#define SC_OPCODE_IF_ERROR 7 +#define SC_OPCODE_IF_NA 8 +#define SC_OPCODE_CHOSE 9 +#define SC_OPCODE_OPEN 10 /* parentheses and separators */ +#define SC_OPCODE_CLOSE 11 +#define SC_OPCODE_SEP 12 +#define SC_OPCODE_MISSING 13 /* special OpCodes */ +#define SC_OPCODE_BAD 14 +#define SC_OPCODE_STRINGXML 15 +#define SC_OPCODE_SPACES 16 +#define SC_OPCODE_MAT_REF 17 +#define SC_OPCODE_DB_AREA 18 /* additional access operators */ +#define SC_OPCODE_MACRO 19 +#define SC_OPCODE_COL_ROW_NAME 20 +#define SC_OPCODE_COL_ROW_NAME_AUTO 21 +#define SC_OPCODE_PERCENT_SIGN 22 /* operator _follows_ value */ +#define SC_OPCODE_ARRAY_OPEN 23 +#define SC_OPCODE_ARRAY_CLOSE 24 +#define SC_OPCODE_ARRAY_ROW_SEP 25 +#define SC_OPCODE_ARRAY_COL_SEP 26 /* some convs use sep != col_sep */ +#define SC_OPCODE_STOP_DIV 27 +#define SC_OPCODE_SKIP 28 /* used to skip raw tokens during string compilation */ /*** error constants #... ***/ #define SC_OPCODE_START_ERRORS 30 diff --git a/formula/inc/formula/errorcodes.hxx b/formula/inc/formula/errorcodes.hxx index a99dc7b9d4e0..1e145e04132b 100644 --- a/formula/inc/formula/errorcodes.hxx +++ b/formula/inc/formula/errorcodes.hxx @@ -72,6 +72,9 @@ const sal_uInt16 errNestedArray = 533; // be used to distinguish that condition from all other (inherited) errors. Do // not use for anything else! Never push or inherit the error otherwise! const sal_uInt16 errNotNumericString = 534; +// ScInterpreter internal: jump matrix already has a result at this position, +// do not overwrite in case of empty code path. +const sal_uInt16 errJumpMatHasResult = 535; // Interpreter: NA() not available condition, not a real error const sal_uInt16 NOTAVAILABLE = 0x7fff; diff --git a/formula/inc/formula/opcode.hxx b/formula/inc/formula/opcode.hxx index cd1831b89637..e37395ea6e70 100644 --- a/formula/inc/formula/opcode.hxx +++ b/formula/inc/formula/opcode.hxx @@ -34,6 +34,8 @@ enum OpCodeEnum ocExternalRef = SC_OPCODE_EXTERNAL_REF, // Jump commands ocIf = SC_OPCODE_IF, + ocIfError = SC_OPCODE_IF_ERROR, + ocIfNA = SC_OPCODE_IF_NA, ocChose = SC_OPCODE_CHOSE, // Parentheses and separators ocOpen = SC_OPCODE_OPEN, diff --git a/formula/source/core/api/FormulaCompiler.cxx b/formula/source/core/api/FormulaCompiler.cxx index f79201b6c673..c40211386828 100644 --- a/formula/source/core/api/FormulaCompiler.cxx +++ b/formula/source/core/api/FormulaCompiler.cxx @@ -433,6 +433,8 @@ uno::Sequence< sheet::FormulaOpCodeMapEntry > FormulaCompiler::OpCodeMap::create // Additional functions not within range of functions. static const sal_uInt16 aOpCodes[] = { SC_OPCODE_IF, + SC_OPCODE_IF_ERROR, + SC_OPCODE_IF_NA, SC_OPCODE_CHOSE, SC_OPCODE_AND, SC_OPCODE_OR, @@ -824,7 +826,7 @@ sal_uInt16 FormulaCompiler::GetErrorConstant( const String& rName ) const { switch ((*iLook).second) { - // Not all may make sense in a formula, but these we know as + // Not all may make sense in a formula, but these we know as // opcodes. case ocErrNull: nError = errNoCode; @@ -1125,7 +1127,8 @@ void FormulaCompiler::Factor() || eOp == ocOr || eOp == ocBad || ( eOp >= ocInternalBegin && eOp <= ocInternalEnd ) - || (bCompileForFAP && ((eOp == ocIf) || (eOp == ocChose))) + || ( bCompileForFAP + && ( eOp == ocIf || eOp == ocIfError || eOp == ocIfNA || eOp == ocChose ) ) ) { pFacToken = mpToken; @@ -1174,14 +1177,25 @@ void FormulaCompiler::Factor() pFacToken->SetByte( nSepCount ); PutCode( pFacToken ); } - else if (eOp == ocIf || eOp == ocChose) + else if (eOp == ocIf || eOp == ocIfError || eOp == ocIfNA || eOp == ocChose) { // the PC counters are -1 pFacToken = mpToken; - if ( eOp == ocIf ) - pFacToken->GetJump()[ 0 ] = 3; // if, else, behind - else - pFacToken->GetJump()[ 0 ] = MAXJUMPCOUNT+1; + switch (eOp) + { + case ocIf: + pFacToken->GetJump()[ 0 ] = 3; // if, else, behind + break; + case ocChose: + pFacToken->GetJump()[ 0 ] = MAXJUMPCOUNT+1; + break; + case ocIfError: + case ocIfNA: + pFacToken->GetJump()[ 0 ] = 2; // if, behind + break; + default: + SAL_WARN( "formula.core", "FormulaCompiler::Factor: forgot to add a jump count case?"); + } eOp = NextToken(); if (eOp == ocOpen) { @@ -1190,14 +1204,30 @@ void FormulaCompiler::Factor() } else SetError(errPairExpected); - short nJumpCount = 0; PutCode( pFacToken ); - // during AutoCorrect (since pArr->GetCodeError() is + // During AutoCorrect (since pArr->GetCodeError() is // ignored) an unlimited ocIf would crash because // ScRawToken::Clone() allocates the JumpBuffer according to - // nJump[0]*2+2, which is 3*2+2 on ocIf. - const short nJumpMax = - (pFacToken->GetOpCode() == ocIf ? 3 : MAXJUMPCOUNT); + // nJump[0]*2+2, which is 3*2+2 on ocIf and 2*2+2 ocIfError and ocIfNA. + short nJumpMax; + OpCode eFacOpCode = pFacToken->GetOpCode(); + switch (eFacOpCode) + { + case ocIf: + nJumpMax = 3; + break; + case ocChose: + nJumpMax = MAXJUMPCOUNT; + break; + case ocIfError: + case ocIfNA: + nJumpMax = 2; + break; + default: + nJumpMax = 0; + SAL_WARN( "formula.core", "FormulaCompiler::Factor: forgot to add a jump max case?"); + } + short nJumpCount = 0; while ( (nJumpCount < (MAXJUMPCOUNT - 1)) && (eOp == ocSep) && (!pArr->GetCodeError() || bIgnoreErrors) ) { @@ -1216,11 +1246,28 @@ void FormulaCompiler::Factor() // always limit to nJumpMax, no arbitrary overwrites if ( ++nJumpCount <= nJumpMax ) pFacToken->GetJump()[ nJumpCount ] = pc-1; - if ((pFacToken->GetOpCode() == ocIf && (nJumpCount > 3)) || - (nJumpCount >= MAXJUMPCOUNT)) - SetError(errIllegalParameter); - else + eFacOpCode = pFacToken->GetOpCode(); + bool bLimitOk; + switch (eFacOpCode) + { + case ocIf: + bLimitOk = (nJumpCount <= 3); + break; + case ocChose: + bLimitOk = (nJumpCount < MAXJUMPCOUNT); /* TODO: check, really <, not <=? */ + break; + case ocIfError: + case ocIfNA: + bLimitOk = (nJumpCount <= 2); + break; + default: + bLimitOk = false; + SAL_WARN( "formula.core", "FormulaCompiler::Factor: forgot to add a jump limit case?"); + } + if (bLimitOk) pFacToken->GetJump()[ 0 ] = nJumpCount; + else + SetError(errIllegalParameter); } } else if ( eOp == ocMissing ) diff --git a/formula/source/core/api/token.cxx b/formula/source/core/api/token.cxx index d187073b2f22..f6110e7ce5b3 100644 --- a/formula/source/core/api/token.cxx +++ b/formula/source/core/api/token.cxx @@ -76,7 +76,7 @@ bool FormulaToken::IsFunction() const eOp != ocColRowNameAuto && eOp != ocName && eOp != ocDBArea && (GetByte() != 0 // x parameters || (SC_OPCODE_START_NO_PAR <= eOp && eOp < SC_OPCODE_STOP_NO_PAR) // no parameter - || (ocIf == eOp || ocChose == eOp ) // @ jump commands + || (ocIf == eOp || ocIfError == eOp || ocIfNA == eOp || ocChose == eOp ) // @ jump commands || (SC_OPCODE_START_1_PAR <= eOp && eOp < SC_OPCODE_STOP_1_PAR) // one parameter || (SC_OPCODE_START_2_PAR <= eOp && eOp < SC_OPCODE_STOP_2_PAR) // x parameters (cByte==0 in // FuncAutoPilot) @@ -91,9 +91,10 @@ bool FormulaToken::IsFunction() const sal_uInt8 FormulaToken::GetParamCount() const { if ( eOp < SC_OPCODE_STOP_DIV && eOp != ocExternal && eOp != ocMacro && - eOp != ocIf && eOp != ocChose && eOp != ocPercentSign ) + eOp != ocIf && eOp != ocIfError && eOp != ocIfNA && eOp != ocChose && + eOp != ocPercentSign ) return 0; // parameters and specials - // ocIf and ocChose not for FAP, have cByte then + // ocIf, ocIfError, ocIfNA and ocChose not for FAP, have cByte then //2do: bool parameter whether FAP or not? else if ( GetByte() ) return GetByte(); // all functions, also ocExternal and ocMacro @@ -106,7 +107,7 @@ sal_uInt8 FormulaToken::GetParamCount() const return 0; // no parameter else if (SC_OPCODE_START_1_PAR <= eOp && eOp < SC_OPCODE_STOP_1_PAR) return 1; // one parameter - else if ( eOp == ocIf || eOp == ocChose ) + else if ( eOp == ocIf || eOp == ocIfError || eOp == ocIfNA || eOp == ocChose ) return 1; // only the condition counts as parameter else return 0; // all the rest, no Parameter, or @@ -842,8 +843,8 @@ bool FormulaTokenArray::HasMatrixDoubleRefOps() } if ( eOp == ocPush || lcl_IsReference( eOp, t->GetType() ) ) pStack[sp++] = t; - else if ( eOp == ocIf || eOp == ocChose ) - { // Jumps ignorieren, vorheriges Result (Condition) poppen + else if ( eOp == ocIf || eOp == ocIfError || eOp == ocIfNA || eOp == ocChose ) + { // ignore Jumps, pop previous Result (Condition) if ( sp ) --sp; } @@ -1178,10 +1179,17 @@ FormulaToken* FormulaTokenArray::AddOpCode( OpCode eOp ) pRet = new FormulaToken( svSep,eOp ); break; case ocIf: + case ocIfError: + case ocIfNA: case ocChose: { short nJump[MAXJUMPCOUNT + 1]; - nJump[ 0 ] = ocIf == eOp ? 3 : MAXJUMPCOUNT+1; + if ( eOp == ocIf ) + nJump[ 0 ] = 3; + else if ( eOp == ocChose ) + nJump[ 0 ] = MAXJUMPCOUNT + 1; + else + nJump[ 0 ] = 2; pRet = new FormulaJumpToken( eOp, (short*)nJump ); } break; diff --git a/formula/source/core/resource/core_resource.src b/formula/source/core/resource/core_resource.src index 8b0ee14e9a5a..d21a7373f9ac 100644 --- a/formula/source/core/resource/core_resource.src +++ b/formula/source/core/resource/core_resource.src @@ -24,6 +24,8 @@ Resource RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF { String SC_OPCODE_IF { Text = "IF" ; }; + String SC_OPCODE_IF_ERROR { Text = "IFERROR" ; }; + String SC_OPCODE_IF_NA { Text = "IFNA" ; }; String SC_OPCODE_CHOSE { Text = "CHOOSE" ; }; String SC_OPCODE_OPEN { Text = "(" ; }; String SC_OPCODE_CLOSE { Text = ")" ; }; @@ -362,6 +364,8 @@ Resource RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF Resource RID_STRLIST_FUNCTION_NAMES_ENGLISH { String SC_OPCODE_IF { Text = "IF" ; }; + String SC_OPCODE_IF_ERROR { Text = "IFERROR" ; }; + String SC_OPCODE_IF_NA { Text = "IFNA" ; }; String SC_OPCODE_CHOSE { Text = "CHOOSE" ; }; String SC_OPCODE_OPEN { Text = "(" ; }; String SC_OPCODE_CLOSE { Text = ")" ; }; @@ -700,6 +704,14 @@ Resource RID_STRLIST_FUNCTION_NAMES { Text [ en-US ] = "IF" ; }; + String SC_OPCODE_IF_ERROR + { + Text [ en-US ] = "IFERROR" ; + }; + String SC_OPCODE_IF_NA + { + Text [ en-US ] = "IFNA" ; + }; String SC_OPCODE_CHOSE { Text [ en-US ] = "CHOOSE" ; |