1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "parclass.hxx"
23 #include "callform.hxx"
24 #include "addincol.hxx"
25 #include "funcdesc.hxx"
26 #include <unotools/charclass.hxx>
27 #include <osl/diagnose.h>
28 #include <sal/macros.h>
31 #if OSL_DEBUG_LEVEL > 1
32 // the documentation thingy
34 #include <com/sun/star/sheet/FormulaLanguage.hpp>
35 #include <rtl/strbuf.hxx>
36 #include "compiler.hxx"
40 /* Following assumptions are made:
41 * - OpCodes not specified at all will have at least one and only parameters of
42 * type Value, no check is done on the count of parameters => no Bounds type
44 * - For OpCodes with a variable number of parameters the type(s) of the last
45 * repeated parameter(s) specified determine(s) the type(s) of all following
49 const ScParameterClassification::RawData
ScParameterClassification::pRawData
[] =
51 // { OpCode, {{ Type, ... }, nRepeatLast }},
53 // IF() and CHOOSE() are somewhat special, since the ScJumpMatrix is
54 // created inside those functions and ConvertMatrixParameters() is not
56 { ocIf
, {{ Array
, Reference
, Reference
}, 0 }},
57 { ocIfError
, {{ Array
, Reference
}, 0 }},
58 { ocIfNA
, {{ Array
, Reference
}, 0 }},
59 { ocChose
, {{ Array
, Reference
}, 1 }},
61 { ocOpen
, {{ Bounds
}, 0 }},
62 { ocClose
, {{ Bounds
}, 0 }},
63 { ocSep
, {{ Bounds
}, 0 }},
64 { ocNoName
, {{ Bounds
}, 0 }},
65 { ocErrCell
, {{ Bounds
}, 0 }},
66 { ocStop
, {{ Bounds
}, 0 }},
67 { ocUnion
, {{ Reference
, Reference
}, 0 }},
68 { ocRange
, {{ Reference
, Reference
}, 0 }},
69 // Functions with Value parameters only but not in resource.
70 { ocBackSolver
, {{ Value
, Value
, Value
}, 0 }},
71 { ocTableOp
, {{ Value
, Value
, Value
, Value
, Value
}, 0 }},
72 // Operators and functions.
73 { ocAdd
, {{ Array
, Array
}, 0 }},
74 { ocAmpersand
, {{ Array
, Array
}, 0 }},
75 { ocAnd
, {{ Reference
}, 1 }},
76 { ocAreas
, {{ Reference
}, 0 }},
77 { ocAveDev
, {{ Reference
}, 1 }},
78 { ocAverage
, {{ Reference
}, 1 }},
79 { ocAverageA
, {{ Reference
}, 1 }},
80 { ocAverageIf
, {{ Reference
, Value
, Reference
}, 0 }},
81 { ocAverageIfs
, {{ Reference
, Reference
, Value
}, 2 }},
82 { ocCell
, {{ Value
, Reference
}, 0 }},
83 { ocColumn
, {{ Reference
}, 0 }},
84 { ocColumns
, {{ Reference
}, 1 }},
85 { ocCorrel
, {{ ForceArray
, ForceArray
}, 0 }},
86 { ocCount
, {{ Reference
}, 1 }},
87 { ocCount2
, {{ Reference
}, 1 }},
88 { ocCountEmptyCells
, {{ Reference
}, 0 }},
89 { ocCountIf
, {{ Reference
, Value
}, 0 }},
90 { ocCountIfs
, {{ Reference
, Value
}, 2 }},
91 { ocCovar
, {{ ForceArray
, ForceArray
}, 0 }},
92 { ocCovarianceP
, {{ ForceArray
, ForceArray
}, 0 }},
93 { ocCovarianceS
, {{ ForceArray
, ForceArray
}, 0 }},
94 { ocDBAverage
, {{ Reference
, Reference
, Reference
}, 0 }},
95 { ocDBCount
, {{ Reference
, Reference
, Reference
}, 0 }},
96 { ocDBCount2
, {{ Reference
, Reference
, Reference
}, 0 }},
97 { ocDBGet
, {{ Reference
, Reference
, Reference
}, 0 }},
98 { ocDBMax
, {{ Reference
, Reference
, Reference
}, 0 }},
99 { ocDBMin
, {{ Reference
, Reference
, Reference
}, 0 }},
100 { ocDBProduct
, {{ Reference
, Reference
, Reference
}, 0 }},
101 { ocDBStdDev
, {{ Reference
, Reference
, Reference
}, 0 }},
102 { ocDBStdDevP
, {{ Reference
, Reference
, Reference
}, 0 }},
103 { ocDBSum
, {{ Reference
, Reference
, Reference
}, 0 }},
104 { ocDBVar
, {{ Reference
, Reference
, Reference
}, 0 }},
105 { ocDBVarP
, {{ Reference
, Reference
, Reference
}, 0 }},
106 { ocDevSq
, {{ Reference
}, 1 }},
107 { ocDiv
, {{ Array
, Array
}, 0 }},
108 { ocEqual
, {{ Array
, Array
}, 0 }},
109 { ocForecast
, {{ Value
, ForceArray
, ForceArray
}, 0 }},
110 { ocFrequency
, {{ Reference
, Reference
}, 0 }},
111 { ocFTest
, {{ ForceArray
, ForceArray
}, 0 }},
112 { ocGeoMean
, {{ Reference
}, 1 }},
113 { ocGCD
, {{ Reference
}, 1 }},
114 { ocGreater
, {{ Array
, Array
}, 0 }},
115 { ocGreaterEqual
, {{ Array
, Array
}, 0 }},
116 { ocGrowth
, {{ Reference
, Reference
, Reference
, Value
}, 0 }},
117 { ocHarMean
, {{ Reference
}, 1 }},
118 { ocHLookup
, {{ Value
, Reference
, Value
, Value
}, 0 }},
119 { ocIRR
, {{ Reference
, Value
}, 0 }},
120 { ocIndex
, {{ Reference
, Value
, Value
, Value
}, 0 }},
121 { ocIntercept
, {{ ForceArray
, ForceArray
}, 0 }},
122 { ocIntersect
, {{ Reference
, Reference
}, 0 }},
123 { ocIsRef
, {{ Reference
}, 0 }},
124 { ocLCM
, {{ Reference
}, 1 }},
125 { ocKurt
, {{ Reference
}, 1 }},
126 { ocLarge
, {{ Reference
, Value
}, 0 }},
127 { ocLess
, {{ Array
, Array
}, 0 }},
128 { ocLessEqual
, {{ Array
, Array
}, 0 }},
129 { ocLookup
, {{ Value
, ReferenceOrForceArray
, ReferenceOrForceArray
}, 0 }},
130 { ocMatch
, {{ Value
, Reference
, Reference
}, 0 }},
131 { ocMatDet
, {{ ForceArray
}, 0 }},
132 { ocMatInv
, {{ ForceArray
}, 0 }},
133 { ocMatMult
, {{ ForceArray
, ForceArray
}, 0 }},
134 { ocMatTrans
, {{ Array
}, 0 }}, // strange, but Xcl doesn't force MatTrans array
135 { ocMatValue
, {{ Reference
, Value
, Value
}, 0 }},
136 { ocMax
, {{ Reference
}, 1 }},
137 { ocMaxA
, {{ Reference
}, 1 }},
138 { ocMedian
, {{ Reference
}, 1 }},
139 { ocMin
, {{ Reference
}, 1 }},
140 { ocMinA
, {{ Reference
}, 1 }},
141 { ocMIRR
, {{ Reference
, Value
, Value
}, 0 }},
142 { ocModalValue
, {{ ForceArray
}, 1 }},
143 { ocMul
, {{ Array
, Array
}, 0 }},
144 { ocMultiArea
, {{ Reference
}, 1 }},
145 { ocNPV
, {{ Value
, Reference
}, 1 }},
146 { ocNeg
, {{ Array
}, 0 }},
147 { ocNegSub
, {{ Array
}, 0 }},
148 { ocNot
, {{ Array
}, 0 }},
149 { ocNotEqual
, {{ Array
, Array
}, 0 }},
150 { ocOffset
, {{ Reference
, Value
, Value
, Value
, Value
}, 0 }},
151 { ocOr
, {{ Reference
}, 1 }},
152 { ocPearson
, {{ ForceArray
, ForceArray
}, 0 }},
153 { ocPercentile
, {{ Reference
, Value
}, 0 }},
154 { ocPercentrank
, {{ Reference
, Value
}, 0 }},
155 { ocPow
, {{ Array
, Array
}, 0 }},
156 { ocPower
, {{ Array
, Array
}, 0 }},
157 { ocProb
, {{ ForceArray
, ForceArray
, Value
, Value
}, 0 }},
158 { ocProduct
, {{ Reference
}, 1 }},
159 { ocQuartile
, {{ Reference
, Value
}, 0 }},
160 { ocRank
, {{ Value
, Reference
, Value
}, 0 }},
161 { ocRGP
, {{ Reference
, Reference
, Value
, Value
}, 0 }},
162 { ocRKP
, {{ Reference
, Reference
, Value
, Value
}, 0 }},
163 { ocRow
, {{ Reference
}, 0 }},
164 { ocRows
, {{ Reference
}, 1 }},
165 { ocRSQ
, {{ ForceArray
, ForceArray
}, 0 }},
166 { ocSchiefe
, {{ Reference
}, 1 }},
167 { ocSkewp
, {{ Reference
}, 1 }},
168 { ocSlope
, {{ ForceArray
, ForceArray
}, 0 }},
169 { ocSmall
, {{ Reference
, Value
}, 0 }},
170 { ocStDev
, {{ Reference
}, 1 }},
171 { ocStDevA
, {{ Reference
}, 1 }},
172 { ocStDevP
, {{ Reference
}, 1 }},
173 { ocStDevPA
, {{ Reference
}, 1 }},
174 { ocStDevP_MS
, {{ Reference
}, 1 }},
175 { ocStDevS
, {{ Reference
}, 1 }},
176 { ocSTEYX
, {{ ForceArray
, ForceArray
}, 0 }},
177 { ocSub
, {{ Array
, Array
}, 0 }},
178 { ocSubTotal
, {{ Value
, Reference
}, 1 }},
179 { ocSum
, {{ Reference
}, 1 }},
180 { ocSumIf
, {{ Reference
, Value
, Reference
}, 0 }},
181 { ocSumIfs
, {{ Reference
, Reference
, Value
}, 2 }},
182 { ocSumProduct
, {{ ForceArray
}, 1 }},
183 { ocSumSQ
, {{ Reference
}, 1 }},
184 { ocSumX2MY2
, {{ ForceArray
, ForceArray
}, 0 }},
185 { ocSumX2DY2
, {{ ForceArray
, ForceArray
}, 0 }},
186 { ocSumXMY2
, {{ ForceArray
, ForceArray
}, 0 }},
187 { ocTable
, {{ Reference
}, 0 }},
188 { ocTables
, {{ Reference
}, 1 }},
189 { ocTrend
, {{ Reference
, Reference
, Reference
, Value
}, 0 }},
190 { ocTrimMean
, {{ Reference
, Value
}, 0 }},
191 { ocTTest
, {{ ForceArray
, ForceArray
, Value
, Value
}, 0 }},
192 { ocVar
, {{ Reference
}, 1 }},
193 { ocVarA
, {{ Reference
}, 1 }},
194 { ocVarP
, {{ Reference
}, 1 }},
195 { ocVarPA
, {{ Reference
}, 1 }},
196 { ocVarP_MS
, {{ Reference
}, 1 }},
197 { ocVarS
, {{ Reference
}, 1 }},
198 { ocVLookup
, {{ Value
, Reference
, Value
, Value
}, 0 }},
199 { ocXor
, {{ Reference
}, 1 }},
200 { ocZTest
, {{ Reference
, Value
, Value
}, 0 }},
202 // ocN, ocT: Excel says (and handles) Reference, error? This means no
203 // position dependent SingleRef if DoubleRef, and no array calculation,
204 // just the upper left corner. We never did that for ocT and now also not
205 // for ocN (position dependent intersection worked before but array
206 // didn't). No specifics in ODFF, so the general rule applies. Gnumeric
208 { ocN
, {{ Value
}, 0 }},
209 { ocT
, {{ Value
}, 0 }},
211 { ocNone
, {{ Bounds
}, 0 } }
214 ScParameterClassification::RunData
* ScParameterClassification::pData
= NULL
;
216 void ScParameterClassification::Init()
220 pData
= new RunData
[ SC_OPCODE_LAST_OPCODE_ID
+ 1 ];
221 memset( pData
, 0, sizeof(RunData
) * (SC_OPCODE_LAST_OPCODE_ID
+ 1));
223 // init from specified static data above
224 for ( size_t i
=0; i
< SAL_N_ELEMENTS(pRawData
); ++i
)
226 const RawData
* pRaw
= &pRawData
[i
];
227 if ( pRaw
->eOp
> SC_OPCODE_LAST_OPCODE_ID
)
229 OSL_ENSURE( pRaw
->eOp
== ocNone
, "RawData OpCode error");
233 RunData
* pRun
= &pData
[ pRaw
->eOp
];
234 #if OSL_DEBUG_LEVEL > 1
235 if ( pRun
->aData
.nParam
[0] != Unknown
)
237 OSL_TRACE( "already assigned: %d", pRaw
->eOp
);
240 memcpy( &(pRun
->aData
), &(pRaw
->aData
), sizeof(CommonData
));
241 // fill 0-initialized fields with real values
242 if ( pRun
->aData
.nRepeatLast
)
244 for ( sal_Int32 j
=0; j
< CommonData::nMaxParams
; ++j
)
246 if ( pRun
->aData
.nParam
[j
] )
247 pRun
->nMinParams
= sal::static_int_cast
<sal_uInt8
>( j
+1 );
248 else if (j
>= pRun
->aData
.nRepeatLast
)
249 pRun
->aData
.nParam
[j
] = pRun
->aData
.nParam
[j
- pRun
->aData
.nRepeatLast
];
252 OSL_TRACE( "bad classification: eOp %d, repeated param %d negative offset", pRaw
->eOp
, j
);
253 pRun
->aData
.nParam
[j
] = Unknown
;
259 for ( sal_Int32 j
=0; j
< CommonData::nMaxParams
; ++j
)
261 if ( !pRun
->aData
.nParam
[j
] )
263 if ( j
== 0 || pRun
->aData
.nParam
[j
-1] != Bounds
)
264 pRun
->nMinParams
= sal::static_int_cast
<sal_uInt8
>( j
);
265 pRun
->aData
.nParam
[j
] = Bounds
;
268 if ( !pRun
->nMinParams
&&
269 pRun
->aData
.nParam
[CommonData::nMaxParams
-1] != Bounds
)
270 pRun
->nMinParams
= CommonData::nMaxParams
;
272 for ( sal_Int32 j
=0; j
< CommonData::nMaxParams
; ++j
)
274 if ( pRun
->aData
.nParam
[j
] == ForceArray
|| pRun
->aData
.nParam
[j
] == ReferenceOrForceArray
)
276 pRun
->bHasForceArray
= true;
283 #if OSL_DEBUG_LEVEL > 1
284 GenerateDocumentation();
288 void ScParameterClassification::Exit()
294 ScParameterClassification::Type
ScParameterClassification::GetParameterType(
295 const formula::FormulaToken
* pToken
, sal_uInt16 nParameter
)
297 OpCode eOp
= pToken
->GetOpCode();
301 return GetExternalParameterType( pToken
, nParameter
);
308 // added to avoid warnings
311 if ( 0 <= (short)eOp
&& eOp
<= SC_OPCODE_LAST_OPCODE_ID
)
315 if ( nParameter
< CommonData::nMaxParams
)
316 eType
= pData
[eOp
].aData
.nParam
[nParameter
];
317 else if ( (nRepeat
= pData
[eOp
].aData
.nRepeatLast
) > 0 )
319 // The usual case is 1 repeated parameter, we don't need to
320 // calculate that on each call.
321 sal_uInt16 nParam
= (nRepeat
> 1 ?
322 (pData
[eOp
].nMinParams
-
323 ((nParameter
- pData
[eOp
].nMinParams
) % nRepeat
)) :
324 pData
[eOp
].nMinParams
);
325 return pData
[eOp
].aData
.nParam
[nParam
];
329 return eType
== Unknown
? Value
: eType
;
334 ScParameterClassification::Type
335 ScParameterClassification::GetExternalParameterType( const formula::FormulaToken
* pToken
,
336 sal_uInt16 nParameter
)
339 // similar to ScInterpreter::ScExternal()
340 OUString aFuncName
= ScGlobal::pCharClass
->uppercase( pToken
->GetExternal());
342 const FuncData
* pFuncData
= ScGlobal::GetFuncCollection()->findByName(aFuncName
);
345 if ( nParameter
>= pFuncData
->GetParamCount() )
349 switch ( pFuncData
->GetParamType( nParameter
) )
357 // also array types are created using an area reference
365 ScGlobal::GetAddInCollection()->FindFunction(aFuncName
, false);
367 if (!aUnoName
.isEmpty())
369 // the relevant parts of ScUnoAddInCall without having to create one
370 const ScUnoAddInFuncData
* pFuncData
=
371 ScGlobal::GetAddInCollection()->GetFuncData( aUnoName
, true ); // need fully initialized data
374 long nCount
= pFuncData
->GetArgumentCount();
379 const ScAddInArgDesc
* pArgs
= pFuncData
->GetArguments();
380 if ( nParameter
>= nCount
&&
381 pArgs
[nCount
-1].eType
== SC_ADDINARG_VARARGS
)
383 // last arg is sequence, optional "any"s, we simply can't
384 // determine the type
385 if ( eRet
== Unknown
)
387 if ( nParameter
>= nCount
)
391 switch ( pArgs
[nParameter
].eType
)
393 case SC_ADDINARG_INTEGER
:
394 case SC_ADDINARG_DOUBLE
:
395 case SC_ADDINARG_STRING
:
409 #if OSL_DEBUG_LEVEL > 1
411 // add remaining functions, all Value parameters
412 void ScParameterClassification::MergeArgumentsFromFunctionResource()
414 ScFunctionList
* pFuncList
= ScGlobal::GetStarCalcFunctionList();
415 for ( const ScFuncDesc
* pDesc
= pFuncList
->First(); pDesc
;
416 pDesc
= pFuncList
->Next() )
418 if ( pDesc
->nFIndex
> SC_OPCODE_LAST_OPCODE_ID
||
419 pData
[pDesc
->nFIndex
].aData
.nParam
[0] != Unknown
)
420 continue; // not an internal opcode or already done
422 RunData
* pRun
= &pData
[ pDesc
->nFIndex
];
423 sal_uInt16 nArgs
= pDesc
->GetSuppressedArgCount();
424 if ( nArgs
>= PAIRED_VAR_ARGS
)
426 nArgs
-= PAIRED_VAR_ARGS
- 2;
427 pRun
->aData
.nRepeatLast
= 2;
429 else if ( nArgs
>= VAR_ARGS
)
431 nArgs
-= VAR_ARGS
- 1;
432 pRun
->aData
.nRepeatLast
= 1;
434 if ( nArgs
> CommonData::nMaxParams
)
437 aBuf
.append("ScParameterClassification::Init: too many arguments in listed function: ");
438 aBuf
.append(OUStringToOString(*(pDesc
->pFuncName
), RTL_TEXTENCODING_UTF8
));
440 aBuf
.append(sal_Int32(nArgs
));
441 OSL_FAIL( aBuf
.getStr());
442 nArgs
= CommonData::nMaxParams
- 1;
443 pRun
->aData
.nRepeatLast
= 1;
445 pRun
->nMinParams
= static_cast< sal_uInt8
>( nArgs
);
446 for ( sal_Int32 j
=0; j
< nArgs
; ++j
)
448 pRun
->aData
.nParam
[j
] = Value
;
450 if ( pRun
->aData
.nRepeatLast
)
452 for ( sal_Int32 j
= nArgs
; j
< CommonData::nMaxParams
; ++j
)
454 pRun
->aData
.nParam
[j
] = Value
;
459 for ( sal_Int32 j
= nArgs
; j
< CommonData::nMaxParams
; ++j
)
461 pRun
->aData
.nParam
[j
] = Bounds
;
467 void ScParameterClassification::GenerateDocumentation()
469 static const sal_Char aEnvVarName
[] = "OOO_CALC_GENPARCLASSDOC";
470 if ( !getenv( aEnvVarName
) )
472 MergeArgumentsFromFunctionResource();
474 ScCompiler
aComp(NULL
,aAddress
);
475 ScCompiler::OpCodeMapPtr
xMap( aComp
.GetOpCodeMap(::com::sun::star::sheet::FormulaLanguage::ENGLISH
));
479 size_t nCount
= xMap
->getSymbolCount();
480 for ( size_t i
=0; i
<nCount
; ++i
)
482 OpCode eOp
= OpCode(i
);
483 if ( !xMap
->getSymbol(eOp
).isEmpty() )
485 fprintf( stdout
, "%s: ", aEnvVarName
);
486 OStringBuffer
aStr(OUStringToOString(xMap
->getSymbol(eOp
), RTL_TEXTENCODING_UTF8
));
488 formula::FormulaByteToken
aToken( eOp
);
489 sal_uInt8 nParams
= GetMinimumParameters( eOp
);
490 // preset parameter count according to opcode value, with some
492 if ( eOp
< SC_OPCODE_STOP_DIV
)
510 else if ( eOp
< SC_OPCODE_STOP_ERRORS
)
512 else if ( eOp
< SC_OPCODE_STOP_BIN_OP
)
518 aToken
.SetByte(1); // (r1)AND(r2) --> AND( r1, ...)
524 else if ( eOp
< SC_OPCODE_STOP_UN_OP
)
526 else if ( eOp
< SC_OPCODE_STOP_NO_PAR
)
528 else if ( eOp
< SC_OPCODE_STOP_1_PAR
)
531 aToken
.SetByte( nParams
);
532 // compare (this is a mere test for opcode order Div, BinOp, UnOp,
533 // NoPar, 1Par, ...) and override parameter count with
535 if ( nParams
!= aToken
.GetByte() )
536 fprintf( stdout
, "(parameter count differs, token Byte: %d classification: %d) ",
537 aToken
.GetByte(), nParams
);
538 aToken
.SetByte( nParams
);
539 if ( nParams
!= aToken
.GetParamCount() )
540 fprintf( stdout
, "(parameter count differs, token ParamCount: %d classification: %d) ",
541 aToken
.GetParamCount(), nParams
);
542 for ( sal_uInt16 j
=0; j
< nParams
; ++j
)
546 Type eType
= GetParameterType( &aToken
, j
);
550 aStr
.append(" Value");
553 aStr
.append(" Reference");
556 aStr
.append(" Array");
559 aStr
.append(" ForceArray");
561 case ReferenceOrForceArray
:
562 aStr
.append(" ReferenceOrForceArray");
565 aStr
.append(" (Bounds, classification error?)");
568 aStr
.append(" (???, classification error?)");
571 if ( HasRepeatParameters( eOp
) )
572 aStr
.append(", ...");
579 aStr
.append(" // RRI in English resource, but ZGZ in English-only section");
582 aStr
.append(" // e.g. combined first parameter of INDEX() function, not a real function");
585 aStr
.append(" // goal seek via menu, not a real function");
588 aStr
.append(" // MULTIPLE.OPERATIONS in English resource, but TABLE in English-only section");
591 aStr
.append(" // error function, not a real function");
595 fprintf( stdout
, "%s\n", aStr
.getStr());
601 #endif // OSL_DEBUG_LEVEL
603 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */