Coverage Report

Created: 2026-04-29 07:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwps/src/lib/QuattroFormula.cpp
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2
/* libwps
3
 * Version: MPL 2.0 / LGPLv2.1+
4
 *
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/.
8
 *
9
 * Major Contributor(s):
10
 * Copyright (C) 2006, 2007 Andrew Ziem
11
 * Copyright (C) 2004 Marc Maurer (uwog@uwog.net)
12
 * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
13
 *
14
 * For minor contributions see the git repository.
15
 *
16
 * Alternatively, the contents of this file may be used under the terms
17
 * of the GNU Lesser General Public License Version 2.1 or later
18
 * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
19
 * applicable instead of those above.
20
 */
21
22
#include <map>
23
#include <sstream>
24
25
#include "WPSStream.h"
26
27
#include "QuattroFormula.h"
28
29
/** namespace to regroup data used to read QuattroPro .wb1-3, .qpw formula */
30
namespace QuattroFormulaInternal
31
{
32
struct Functions
33
{
34
  char const *m_name;
35
  int m_arity;
36
};
37
38
struct State
39
{
40
  /** constructor */
41
  State(QuattroFormulaManager::CellReferenceFunction const &readCellReference, int version)
42
71.7k
    : m_readCellReferenceFunction(readCellReference)
43
71.7k
    , m_version(version)
44
71.7k
    , m_idFunctionsMap()
45
71.7k
    , m_idToDLLName1Map()
46
71.7k
    , m_actDLLName1Id(-1)
47
71.7k
    , m_idToDLLName2Map()
48
71.7k
  {
49
71.7k
    if (m_version>=2)
50
23.9k
    {
51
      // in .qpw, H/VLookUp have four arguments
52
23.9k
      m_idFunctionsMap =
53
23.9k
      {
54
23.9k
        {0x55, {"VLookUp", 4}},
55
23.9k
        {0x5a, {"HLookup", 4}}
56
23.9k
      };
57
23.9k
    }
58
71.7k
  }
59
60
  /** function to call to read a cell reference*/
61
  QuattroFormulaManager::CellReferenceFunction m_readCellReferenceFunction;
62
  /** the file version: 1: .wb1-3, 2: .qpw*/
63
  int m_version;
64
  /** the function which differs from default */
65
  std::map<int, Functions> m_idFunctionsMap;
66
  //! map id to DLL name 1
67
  std::map<int, librevenge::RVNGString> m_idToDLLName1Map;
68
  //! the current id DLL name 1
69
  int m_actDLLName1Id;
70
  //! map id to DLL name2
71
  std::map<Vec2i, librevenge::RVNGString> m_idToDLLName2Map;
72
};
73
}
74
75
// constructor
76
QuattroFormulaManager::QuattroFormulaManager(QuattroFormulaManager::CellReferenceFunction const &readCellReference, int version)
77
71.7k
  : m_state(new QuattroFormulaInternal::State(readCellReference, version))
78
71.7k
{
79
71.7k
}
80
81
void QuattroFormulaManager::addDLLIdName(int id, librevenge::RVNGString const &name, bool func1)
82
233k
{
83
233k
  if (name.empty())
84
2.04k
  {
85
2.04k
    WPS_DEBUG_MSG(("QuattroFormulaManager::addDLLIdName: called with empty name for id=%d\n", id));
86
2.04k
    return;
87
2.04k
  }
88
231k
  if (func1)
89
14.8k
  {
90
14.8k
    m_state->m_actDLLName1Id=id;
91
14.8k
    auto &map = m_state->m_idToDLLName1Map;
92
14.8k
    if (map.find(id) != map.end())
93
6.97k
    {
94
6.97k
      WPS_DEBUG_MSG(("QuattroFormulaManager::addDLLIdName: called with dupplicated id=%d\n", id));
95
6.97k
    }
96
7.86k
    else
97
7.86k
      map[id]=name;
98
14.8k
    return;
99
14.8k
  }
100
216k
  if (m_state->m_actDLLName1Id<0)
101
9.86k
  {
102
9.86k
    WPS_DEBUG_MSG(("QuattroFormulaManager::addDLLIdName: oops, unknown name1 id for %d\n", id));
103
9.86k
    return;
104
9.86k
  }
105
206k
  auto &map = m_state->m_idToDLLName2Map;
106
206k
  Vec2i fId(m_state->m_actDLLName1Id, id);
107
206k
  if (map.find(fId) != map.end())
108
60.5k
  {
109
60.5k
    WPS_DEBUG_MSG(("QuattroFormulaManager::addDLLIdName: called with dupplicated id=%d,%d\n", m_state->m_actDLLName1Id, id));
110
60.5k
  }
111
146k
  else
112
146k
    map[fId]=name;
113
206k
  return;
114
216k
}
115
116
//------------------------------------------------------------
117
// read a formula
118
//------------------------------------------------------------
119
namespace QuattroFormulaInternal
120
{
121
static Functions const s_listFunctions[] =
122
{
123
  // 0
124
  { "", 0} /*SPEC: double*/, {"", 0}/*SPEC: cell*/, {"", 0}/*SPEC: cells*/, {"=", 1} /*SPEC: end of formula*/,
125
  { "(", 1} /* SPEC: () */, {"", 0}/*SPEC: int*/, { "", -2} /*SPEC: text*/, {"", -2} /*SPEC: default argument*/,
126
  { "-", 1}, {"+", 2}, {"-", 2}, {"*", 2},
127
  { "/", 2}, { "^", 2}, {"=", 2}, {"<>", 2},
128
129
  // 1
130
  { "<=", 2},{ ">=", 2},{ "<", 2},{ ">", 2},
131
  { "And", 2},{ "Or", 2}, { "Not", 1}, { "+", 1},
132
  { "&", 2}, { "", -2} /*halt*/, { "DLL", 0} /*DLL*/,{ "", -2} /*extended noop: 1b00011c020400020000000 means A*/,
133
  { "", -2} /*extended op*/,{ "", -2} /*reserved*/,{ "", -2} /*reserved*/,{ "NA", 0} /*checkme*/,
134
135
  // 2
136
  { "NA", 0} /* Error*/,{ "Abs", 1},{ "Int", 1},{ "Sqrt", 1},
137
  { "Log10", 1},{ "Ln", 1},{ "Pi", 0},{ "Sin", 1},
138
  { "Cos", 1},{ "Tan", 1},{ "Atan2", 2},{ "Atan", 1},
139
  { "Asin", 1},{ "Acos", 1},{ "Exp", 1},{ "Mod", 2},
140
141
  // 3
142
  { "Choose", -1},{ "IsNa", 1},{ "IsError", 1},{ "False", 0},
143
  { "True", 0},{ "Rand", 0},{ "Date", 3},{ "Now", 0},
144
  { "PMT", 3} /*BAD*/,{ "QPRO_PV", 3} /*BAD*/,{ "QPRO_FV", 3} /*BAD*/,{ "IF", 3},
145
  { "Day", 1},{ "Month", 1},{ "Year", 1},{ "Round", 2},
146
147
  // 4
148
  { "Time", 3},{ "Hour", 1},{ "Minute", 1},{ "Second", 1},
149
  { "IsNumber", 1},{ "IsText", 1},{ "Len", 1},{ "Value", 1},
150
  { "Fixed", 2}, { "Mid", 3}, { "Char", 1},{ "Ascii", 1},
151
  { "Find", 3},{ "DateValue", 1} /*checkme*/,{ "TimeValue", 1} /*checkme*/,{ "CellPointer", 1} /*checkme*/,
152
153
  // 5
154
  { "Sum", -1},{ "Average", -1},{ "COUNT", -1},{ "Min", -1},
155
  { "Max", -1},{ "VLookUp", 3},{ "NPV", 2}, { "Var", -1},
156
  { "StDev", -1},{ "IRR", 2} /*BAD*/, { "HLookup", 3},{ "DSum", 3},
157
  { "DAverage", 3},{ "DCount", 3},{ "DMin", 3},{ "DMax", 3},
158
159
  // 6
160
  { "DVar", 3},{ "DStd", 3},{ "Index", 3} /* index2d*/, { "Columns", 1},
161
  { "Rows", 1},{ "Rept", 2},{ "Upper", 1},{ "Lower", 1},
162
  { "Left", 2},{ "Right", 2},{ "Replace", 4}, { "Proper", 1},
163
  { "Cell", 2},{ "Trim", 1},{ "Clean", 1},{ "IsText", 1},
164
165
  // 7
166
  { "IsNonText", 1},{ "Exact", 2},{ "QPRO_Call", -2} /*UNKN*/,{ "Indirect", 1},
167
  { "RRI", 3}, { "TERM", 3}, { "CTERM", 3}, { "SLN", 3},
168
  { "SYD", 4},{ "DDB", 4}, { "StDevP", -1}, { "VarP", -1},
169
  { "DBStdDevP", 3}, { "DBVarP", 3}, { "PV", 5}, { "PMT", 5},
170
171
  // 8
172
  { "FV", 5}, { "Nper", 5}, { "Rate", 5}/*IRate*/, { "Ipmt", 6},
173
  { "Ppmt", 6}, { "SumProduct", 2}, { "QPRO_MemAvail", 0}, { "QPRO_MememsAvail", 0},
174
  { "QPRO_FileExist", 1}, { "QPRO_CurValue", 2}, { "Degrees", 1},{ "Radians", 1},
175
  { "QPRO_Hex", 1},{ "QPRO_Num", 1},{ "Today", 0},{ "NPV", 2},
176
177
  // 9
178
  { "QPRO_CellIndex", 4}, { "QPRO_Version", 0}, { "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,
179
  { "QPRO_Dhol", 3} /* fixme name: DHOL ?*/, { "", -2} /*UNKN*/, { "", -2} /*UNKN*/,{ "", -2} /*UNKN*/,
180
  { "", -2} /*UNKN*/,{ "", -2} /*UNKN*/, { "Sheet", 1}, { "", -2} /*UNKN*/,
181
  { "", -2} /*UNKN*/,{ "Index", 4}, { "QPRO_CellIndex3d", -2} /*UNKN*/,{ "QPRO_property", 1},
182
183
  // a
184
  {"QPRO_DDE", 4}, {"QPRO_Command", 1}, {"QPRO_Gerlinie", 3} /* fixme: name GERLINIE? */
185
};
186
187
}
188
189
bool QuattroFormulaManager::readFormula(std::shared_ptr<WPSStream> const &stream, long endPos,
190
                                        Vec2i const &position, int sheetId,
191
                                        std::vector<WKSContentListener::FormulaInstruction> &formula, std::string &error) const
192
53.5k
{
193
53.5k
  RVNGInputStreamPtr input = stream->m_input;
194
53.5k
  libwps::DebugFile &ascFile=stream->m_ascii;
195
53.5k
  formula.resize(0);
196
53.5k
  error = "";
197
53.5k
  long pos = input->tell();
198
53.5k
  if (endPos - pos < 4) return false;
199
53.1k
  auto sz = int(libwps::readU16(input)); // max 1024
200
53.1k
  if (endPos-pos-4 != sz) return false;
201
202
29.2k
  std::vector<QuattroFormulaInternal::CellReference> listCellsPos;
203
29.2k
  auto fieldPos= int(libwps::readU16(input)); // ref begin
204
29.2k
  if (fieldPos<0||fieldPos>sz)
205
1.43k
  {
206
1.43k
    WPS_DEBUG_MSG(("QuattroFormulaManager::readFormula: can not find the field header\n"));
207
1.43k
    error="###fieldPos";
208
1.43k
    return false;
209
1.43k
  }
210
27.8k
  if (fieldPos!=sz)
211
19.8k
  {
212
19.8k
    input->seek(pos+4+fieldPos, librevenge::RVNG_SEEK_SET);
213
19.8k
    ascFile.addDelimiter(pos+4+fieldPos,'|');
214
44.7k
    while (!input->isEnd())
215
44.7k
    {
216
44.7k
      long actPos=input->tell();
217
44.7k
      if (actPos+4>endPos) break;
218
30.0k
      QuattroFormulaInternal::CellReference cell;
219
30.0k
      if (!m_state->m_readCellReferenceFunction(stream, endPos, cell, position, sheetId) || input->tell()<actPos+2)
220
5.07k
      {
221
5.07k
        input->seek(actPos, librevenge::RVNG_SEEK_SET);
222
5.07k
        break;
223
5.07k
      }
224
24.9k
      if (cell.empty())
225
1.86k
      {
226
1.86k
        WPS_DEBUG_MSG(("QuattroFormulaManager::readFormula: find some deleted cells\n"));
227
1.86k
      }
228
23.0k
      else
229
23.0k
        listCellsPos.push_back(cell);
230
24.9k
    }
231
19.8k
    if (input->tell() !=endPos)
232
6.40k
    {
233
6.40k
      ascFile.addDelimiter(input->tell(),'@');
234
6.40k
      static bool first=true;
235
6.40k
      if (first)
236
5
      {
237
5
        WPS_DEBUG_MSG(("QuattroFormulaManager::readFormula: potential formula codes\n"));
238
5
        first=false;
239
5
      }
240
6.40k
      error="###codes,";
241
6.40k
    }
242
19.8k
    input->seek(pos+4, librevenge::RVNG_SEEK_SET);
243
19.8k
    endPos=pos+4+fieldPos;
244
19.8k
  }
245
27.8k
  std::stringstream f;
246
27.8k
  std::vector<std::vector<WKSContentListener::FormulaInstruction> > stack;
247
27.8k
  bool ok = true;
248
27.8k
  size_t actCellId=0;
249
27.8k
  int numDefault=0;
250
136k
  while (long(input->tell()) != endPos)
251
134k
  {
252
134k
    double val;
253
134k
    bool isNaN;
254
134k
    pos = input->tell();
255
134k
    if (pos > endPos) return false;
256
134k
    auto wh = int(libwps::readU8(input));
257
134k
    int arity = 0;
258
134k
    WKSContentListener::FormulaInstruction instr;
259
134k
    bool noInstr=false;
260
134k
    switch (wh)
261
134k
    {
262
12.2k
    case 0x0:
263
12.2k
      if (endPos-pos<9 || !libwps::readDouble8(input, val, isNaN))
264
2.67k
      {
265
2.67k
        f.str("");
266
2.67k
        f << "###number";
267
2.67k
        error=f.str();
268
2.67k
        ok = false;
269
2.67k
        break;
270
2.67k
      }
271
9.59k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Double;
272
9.59k
      instr.m_doubleValue=val;
273
9.59k
      break;
274
16.7k
    case 0x1:
275
16.7k
      if (actCellId>=listCellsPos.size())
276
1.80k
      {
277
1.80k
        f.str("");
278
1.80k
        f << "###unknCell" << actCellId;
279
1.80k
        error=f.str();
280
1.80k
        ok = false;
281
1.80k
        break;
282
1.80k
      }
283
14.9k
      stack.push_back(listCellsPos[actCellId++].m_cells);
284
14.9k
      noInstr=true;
285
14.9k
      break;
286
3.03k
    case 0x2:
287
3.03k
      if (actCellId>=listCellsPos.size())
288
792
      {
289
792
        f.str("");
290
792
        f << "###unknListCell" << actCellId;
291
792
        error=f.str();
292
792
        ok = false;
293
792
        break;
294
792
      }
295
2.23k
      stack.push_back(listCellsPos[actCellId++].m_cells);
296
2.23k
      noInstr=true;
297
2.23k
      break;
298
14.3k
    case 0x5:
299
14.3k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Long;
300
14.3k
      instr.m_longValue=long(libwps::read16(input));
301
14.3k
      break;
302
8.57k
    case 0x6:
303
8.57k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Text;
304
71.5k
      while (!input->isEnd())
305
71.5k
      {
306
71.5k
        if (input->tell() >= endPos)
307
455
        {
308
455
          ok=false;
309
455
          break;
310
455
        }
311
71.0k
        auto c = char(libwps::readU8(input));
312
71.0k
        if (c==0) break;
313
62.9k
        instr.m_content += c;
314
62.9k
      }
315
8.57k
      break;
316
1.93k
    case 0x7: // maybe default parameter
317
1.93k
      ++numDefault;
318
1.93k
      noInstr=true;
319
1.93k
      break;
320
3.75k
    case 0x1a:
321
3.75k
    {
322
3.75k
      if (input->tell()+4 >= endPos)
323
39
      {
324
39
        ok=false;
325
39
        break;
326
39
      }
327
3.71k
      static bool first=true;
328
3.71k
      if (first)
329
4
      {
330
4
        WPS_DEBUG_MSG(("QuattroFormulaManager::readFormula: this file contains some DLL functions, the result can be bad\n"));
331
4
        first=false;
332
4
      }
333
3.71k
      arity= int(libwps::read8(input));
334
3.71k
      std::stringstream s;
335
3.71k
      s << "DLL";
336
3.71k
      int ids[2];
337
7.42k
      for (auto &id : ids) id=int(libwps::readU16(input));
338
3.71k
      s << "_";
339
3.71k
      auto it1 = m_state->m_idToDLLName1Map.find(ids[0]);
340
3.71k
      if (it1!= m_state->m_idToDLLName1Map.end())
341
1.99k
        s << it1->second.cstr();
342
1.71k
      else
343
1.71k
      {
344
1.71k
        WPS_DEBUG_MSG(("QuattroFormulaManager::readFormula: can not find DLL function0 name for id=%d\n", ids[0]));
345
1.71k
        s << "F" << ids[0];
346
1.71k
        f << "##DLLFunc0=" << ids[0] << ",";
347
1.71k
      }
348
3.71k
      s << "_";
349
3.71k
      auto it2 = m_state->m_idToDLLName2Map.find(Vec2i(ids[0],ids[1]));
350
3.71k
      if (it2!= m_state->m_idToDLLName2Map.end())
351
1.95k
        s << it2->second.cstr();
352
1.76k
      else
353
1.76k
      {
354
1.76k
        WPS_DEBUG_MSG(("QuattroFormulaManager::readFormula: can not find DLL function1 name for id=%d\n", ids[1]));
355
1.76k
        s << "F" << ids[1];
356
1.76k
        f << "##DLLFunc1=" << ids[1] << ",";
357
1.76k
      }
358
3.71k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Function;
359
3.71k
      instr.m_content=s.str();
360
3.71k
      break;
361
3.75k
    }
362
73.6k
    default:
363
73.6k
    {
364
73.6k
      auto fIt=m_state->m_idFunctionsMap.find(wh);
365
73.6k
      if (fIt!=m_state->m_idFunctionsMap.end())
366
55
      {
367
55
        instr.m_type=WKSContentListener::FormulaInstruction::F_Function;
368
55
        instr.m_content=fIt->second.m_name;
369
55
        arity = fIt->second.m_arity;
370
55
      }
371
73.5k
      else if (unsigned(wh) >= WPS_N_ELEMENTS(QuattroFormulaInternal::s_listFunctions) || QuattroFormulaInternal::s_listFunctions[wh].m_arity == -2)
372
757
      {
373
757
        f.str("");
374
757
        f << "##Funct" << std::hex << wh;
375
757
        error=f.str();
376
757
        ok = false;
377
757
        break;
378
757
      }
379
72.8k
      else
380
72.8k
      {
381
72.8k
        instr.m_type=WKSContentListener::FormulaInstruction::F_Function;
382
72.8k
        instr.m_content=QuattroFormulaInternal::s_listFunctions[wh].m_name;
383
72.8k
        arity = QuattroFormulaInternal::s_listFunctions[wh].m_arity;
384
72.8k
      }
385
72.8k
      ok=!instr.m_content.empty();
386
72.8k
      if (arity == -1) arity = int(libwps::read8(input));
387
72.8k
      break;
388
73.6k
    }
389
134k
    }
390
391
134k
    if (!ok) break;
392
127k
    if (noInstr) continue;
393
108k
    std::vector<WKSContentListener::FormulaInstruction> child;
394
108k
    if (instr.m_type!=WKSContentListener::FormulaInstruction::F_Function)
395
32.1k
    {
396
32.1k
      child.push_back(instr);
397
32.1k
      stack.push_back(child);
398
32.1k
      continue;
399
32.1k
    }
400
76.5k
    size_t numElt = stack.size();
401
76.5k
    arity-=numDefault;
402
76.5k
    numDefault=0;
403
76.5k
    if (arity<0 || int(numElt) < arity)
404
1.55k
    {
405
1.55k
      f.str("");
406
1.55k
      f << instr.m_content << "[##" << arity << "]";
407
1.55k
      error=f.str();
408
1.55k
      ok = false;
409
1.55k
      break;
410
1.55k
    }
411
    //
412
    // first treat the special cases
413
    //
414
75.0k
    if (arity==3 && instr.m_type==WKSContentListener::FormulaInstruction::F_Function && instr.m_content=="TERM")
415
2.01k
    {
416
      // @TERM(pmt,pint,fv) -> NPER(pint,-pmt,pv=0,fv)
417
2.01k
      auto pmt=stack[size_t(int(numElt)-3)];
418
2.01k
      auto pint=stack[size_t(int(numElt)-2)];
419
2.01k
      auto fv=stack[size_t(int(numElt)-1)];
420
421
2.01k
      stack.resize(size_t(++numElt));
422
      // pint
423
2.01k
      stack[size_t(int(numElt)-4)]=pint;
424
      //-pmt
425
2.01k
      auto &node=stack[size_t(int(numElt)-3)];
426
2.01k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
427
2.01k
      instr.m_content="-";
428
2.01k
      node.resize(0);
429
2.01k
      node.push_back(instr);
430
2.01k
      instr.m_content="(";
431
2.01k
      node.push_back(instr);
432
2.01k
      node.insert(node.end(), pmt.begin(), pmt.end());
433
2.01k
      instr.m_content=")";
434
2.01k
      node.push_back(instr);
435
      //pv=zero
436
2.01k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Long;
437
2.01k
      instr.m_longValue=0;
438
2.01k
      stack[size_t(int(numElt)-2)].resize(0);
439
2.01k
      stack[size_t(int(numElt)-2)].push_back(instr);
440
      //fv
441
2.01k
      stack[size_t(int(numElt)-1)]=fv;
442
2.01k
      arity=4;
443
2.01k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Function;
444
2.01k
      instr.m_content="NPER";
445
2.01k
    }
446
73.0k
    else if (arity==3 && instr.m_type==WKSContentListener::FormulaInstruction::F_Function && instr.m_content=="CTERM")
447
1.35k
    {
448
      // @CTERM(pint,fv,pv) -> NPER(pint,pmt=0,-pv,fv)
449
1.35k
      auto pint=stack[size_t(int(numElt)-3)];
450
1.35k
      auto fv=stack[size_t(int(numElt)-2)];
451
1.35k
      auto pv=stack[size_t(int(numElt)-1)];
452
1.35k
      stack.resize(size_t(++numElt));
453
      // pint
454
1.35k
      stack[size_t(int(numElt)-4)]=pint;
455
      // pmt=0
456
1.35k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Long;
457
1.35k
      instr.m_longValue=0;
458
1.35k
      stack[size_t(int(numElt)-3)].resize(0);
459
1.35k
      stack[size_t(int(numElt)-3)].push_back(instr);
460
      // -pv
461
1.35k
      auto &node=stack[size_t(int(numElt)-2)];
462
1.35k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
463
1.35k
      instr.m_content="-";
464
1.35k
      node.resize(0);
465
1.35k
      node.push_back(instr);
466
1.35k
      instr.m_content="(";
467
1.35k
      node.push_back(instr);
468
1.35k
      node.insert(node.end(), pv.begin(), pv.end());
469
1.35k
      instr.m_content=")";
470
1.35k
      node.push_back(instr);
471
472
      //fv
473
1.35k
      stack[size_t(int(numElt)-1)]=fv;
474
1.35k
      arity=4;
475
1.35k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Function;
476
1.35k
      instr.m_content="NPER";
477
1.35k
    }
478
479
75.0k
    if ((instr.m_content[0] >= 'A' && instr.m_content[0] <= 'Z') || instr.m_content[0] == '(')
480
46.0k
    {
481
46.0k
      if (instr.m_content[0] != '(')
482
44.7k
        child.push_back(instr);
483
484
46.0k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
485
46.0k
      instr.m_content="(";
486
46.0k
      child.push_back(instr);
487
94.6k
      for (int i = 0; i < arity; i++)
488
48.5k
      {
489
48.5k
        if (i)
490
27.2k
        {
491
27.2k
          instr.m_content=";";
492
27.2k
          child.push_back(instr);
493
27.2k
        }
494
48.5k
        auto const &node=stack[size_t(int(numElt)-arity+i)];
495
48.5k
        child.insert(child.end(), node.begin(), node.end());
496
48.5k
      }
497
46.0k
      instr.m_content=")";
498
46.0k
      child.push_back(instr);
499
500
46.0k
      stack.resize(size_t(int(numElt)-arity+1));
501
46.0k
      stack[size_t(int(numElt)-arity)] = child;
502
46.0k
      continue;
503
46.0k
    }
504
28.9k
    if (arity==1)
505
20.5k
    {
506
20.5k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
507
20.5k
      stack[numElt-1].insert(stack[numElt-1].begin(), instr);
508
20.5k
      if (wh==3)
509
17.0k
        break;
510
3.51k
      continue;
511
20.5k
    }
512
8.39k
    if (arity==2)
513
7.76k
    {
514
7.76k
      instr.m_type=WKSContentListener::FormulaInstruction::F_Operator;
515
7.76k
      stack[numElt-2].push_back(instr);
516
7.76k
      stack[numElt-2].insert(stack[numElt-2].end(), stack[numElt-1].begin(), stack[numElt-1].end());
517
7.76k
      stack.resize(numElt-1);
518
7.76k
      continue;
519
7.76k
    }
520
630
    ok=false;
521
630
    error = "### unexpected arity";
522
630
    break;
523
8.39k
  }
524
525
27.5k
  if (!ok) ;
526
18.8k
  else if (stack.size()==1 && stack[0].size()>1 && stack[0][0].m_content=="=")
527
16.9k
  {
528
16.9k
    formula.insert(formula.begin(),stack[0].begin()+1,stack[0].end());
529
16.9k
    if (input->tell()!=endPos)
530
116
    {
531
      // unsure, find some text here, maybe some note
532
116
      static bool first=true;
533
116
      if (first)
534
4
      {
535
4
        WPS_DEBUG_MSG(("QuattroFormulaManager::readFormula: find some extra data\n"));
536
4
        first=false;
537
4
      }
538
116
      error="##extra data";
539
116
      ascFile.addDelimiter(input->tell(),'#');
540
116
    }
541
16.9k
    return true;
542
16.9k
  }
543
1.88k
  else
544
1.88k
    error = "###stack problem";
545
546
10.5k
  static bool first = true;
547
10.5k
  if (first)
548
5
  {
549
5
    WPS_DEBUG_MSG(("QuattroFormulaManager::readFormula: I can not read some formula\n"));
550
5
    first = false;
551
5
  }
552
553
10.5k
  f.str("");
554
10.5k
  for (auto const &i : stack)
555
24.9k
  {
556
24.9k
    for (auto const &j : i)
557
166k
      f << j << ",";
558
24.9k
    f << "@";
559
24.9k
  }
560
10.5k
  f << error << "###";
561
10.5k
  error = f.str();
562
10.5k
  return false;
563
27.5k
}
564
////////////////////////////////////////////////////////////
565
// cell reference
566
////////////////////////////////////////////////////////////
567
namespace QuattroFormulaInternal
568
{
569
std::ostream &operator<<(std::ostream &o, CellReference const &ref)
570
0
{
571
0
  if (ref.m_cells.size()==1)
572
0
  {
573
0
    o << ref.m_cells[0];
574
0
    return o;
575
0
  }
576
0
  o << "[";
577
0
  for (auto const &r: ref.m_cells) o << r;
578
0
  o << "]";
579
0
  return o;
580
0
}
581
}
582
583
/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
584