/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 | | |