1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53: 54: 55: 56: 57: 58: 59:
60: class PHPExcel_Writer_Excel5_Parser
61: {
62:
63:
64:
65:
66:
67:
68: const REGEX_SHEET_TITLE_UNQUOTED = '[^\*\:\/\\\\\?\[\]\+\-\% \\\'\^\&\<\>\=\,\;\#\(\)\"\{\}]+';
69:
70:
71:
72:
73:
74: const REGEX_SHEET_TITLE_QUOTED = '(([^\*\:\/\\\\\?\[\]\\\'])+|(\\\'\\\')+)+';
75:
76: 77: 78: 79:
80: public $_current_char;
81:
82: 83: 84: 85:
86: public $_current_token;
87:
88: 89: 90: 91:
92: public $_formula;
93:
94: 95: 96: 97:
98: public $_lookahead;
99:
100: 101: 102: 103:
104: public $_parse_tree;
105:
106: 107: 108: 109:
110: public $_ext_sheets;
111:
112: 113: 114: 115:
116: public $_references;
117:
118: 119: 120: 121:
122: public function __construct()
123: {
124: $this->_current_char = 0;
125: $this->_current_token = '';
126: $this->_formula = '';
127: $this->_lookahead = '';
128: $this->_parse_tree = '';
129: $this->_initializeHashes();
130: $this->_ext_sheets = array();
131: $this->_references = array();
132: }
133:
134: 135: 136: 137: 138:
139: function _initializeHashes()
140: {
141:
142: $this->ptg = array(
143: 'ptgExp' => 0x01,
144: 'ptgTbl' => 0x02,
145: 'ptgAdd' => 0x03,
146: 'ptgSub' => 0x04,
147: 'ptgMul' => 0x05,
148: 'ptgDiv' => 0x06,
149: 'ptgPower' => 0x07,
150: 'ptgConcat' => 0x08,
151: 'ptgLT' => 0x09,
152: 'ptgLE' => 0x0A,
153: 'ptgEQ' => 0x0B,
154: 'ptgGE' => 0x0C,
155: 'ptgGT' => 0x0D,
156: 'ptgNE' => 0x0E,
157: 'ptgIsect' => 0x0F,
158: 'ptgUnion' => 0x10,
159: 'ptgRange' => 0x11,
160: 'ptgUplus' => 0x12,
161: 'ptgUminus' => 0x13,
162: 'ptgPercent' => 0x14,
163: 'ptgParen' => 0x15,
164: 'ptgMissArg' => 0x16,
165: 'ptgStr' => 0x17,
166: 'ptgAttr' => 0x19,
167: 'ptgSheet' => 0x1A,
168: 'ptgEndSheet' => 0x1B,
169: 'ptgErr' => 0x1C,
170: 'ptgBool' => 0x1D,
171: 'ptgInt' => 0x1E,
172: 'ptgNum' => 0x1F,
173: 'ptgArray' => 0x20,
174: 'ptgFunc' => 0x21,
175: 'ptgFuncVar' => 0x22,
176: 'ptgName' => 0x23,
177: 'ptgRef' => 0x24,
178: 'ptgArea' => 0x25,
179: 'ptgMemArea' => 0x26,
180: 'ptgMemErr' => 0x27,
181: 'ptgMemNoMem' => 0x28,
182: 'ptgMemFunc' => 0x29,
183: 'ptgRefErr' => 0x2A,
184: 'ptgAreaErr' => 0x2B,
185: 'ptgRefN' => 0x2C,
186: 'ptgAreaN' => 0x2D,
187: 'ptgMemAreaN' => 0x2E,
188: 'ptgMemNoMemN' => 0x2F,
189: 'ptgNameX' => 0x39,
190: 'ptgRef3d' => 0x3A,
191: 'ptgArea3d' => 0x3B,
192: 'ptgRefErr3d' => 0x3C,
193: 'ptgAreaErr3d' => 0x3D,
194: 'ptgArrayV' => 0x40,
195: 'ptgFuncV' => 0x41,
196: 'ptgFuncVarV' => 0x42,
197: 'ptgNameV' => 0x43,
198: 'ptgRefV' => 0x44,
199: 'ptgAreaV' => 0x45,
200: 'ptgMemAreaV' => 0x46,
201: 'ptgMemErrV' => 0x47,
202: 'ptgMemNoMemV' => 0x48,
203: 'ptgMemFuncV' => 0x49,
204: 'ptgRefErrV' => 0x4A,
205: 'ptgAreaErrV' => 0x4B,
206: 'ptgRefNV' => 0x4C,
207: 'ptgAreaNV' => 0x4D,
208: 'ptgMemAreaNV' => 0x4E,
209: 'ptgMemNoMemN' => 0x4F,
210: 'ptgFuncCEV' => 0x58,
211: 'ptgNameXV' => 0x59,
212: 'ptgRef3dV' => 0x5A,
213: 'ptgArea3dV' => 0x5B,
214: 'ptgRefErr3dV' => 0x5C,
215: 'ptgAreaErr3d' => 0x5D,
216: 'ptgArrayA' => 0x60,
217: 'ptgFuncA' => 0x61,
218: 'ptgFuncVarA' => 0x62,
219: 'ptgNameA' => 0x63,
220: 'ptgRefA' => 0x64,
221: 'ptgAreaA' => 0x65,
222: 'ptgMemAreaA' => 0x66,
223: 'ptgMemErrA' => 0x67,
224: 'ptgMemNoMemA' => 0x68,
225: 'ptgMemFuncA' => 0x69,
226: 'ptgRefErrA' => 0x6A,
227: 'ptgAreaErrA' => 0x6B,
228: 'ptgRefNA' => 0x6C,
229: 'ptgAreaNA' => 0x6D,
230: 'ptgMemAreaNA' => 0x6E,
231: 'ptgMemNoMemN' => 0x6F,
232: 'ptgFuncCEA' => 0x78,
233: 'ptgNameXA' => 0x79,
234: 'ptgRef3dA' => 0x7A,
235: 'ptgArea3dA' => 0x7B,
236: 'ptgRefErr3dA' => 0x7C,
237: 'ptgAreaErr3d' => 0x7D
238: );
239:
240:
241:
242:
243:
244:
245:
246:
247:
248:
249:
250:
251:
252:
253: $this->_functions = array(
254:
255: 'COUNT' => array( 0, -1, 0, 0 ),
256: 'IF' => array( 1, -1, 1, 0 ),
257: 'ISNA' => array( 2, 1, 1, 0 ),
258: 'ISERROR' => array( 3, 1, 1, 0 ),
259: 'SUM' => array( 4, -1, 0, 0 ),
260: 'AVERAGE' => array( 5, -1, 0, 0 ),
261: 'MIN' => array( 6, -1, 0, 0 ),
262: 'MAX' => array( 7, -1, 0, 0 ),
263: 'ROW' => array( 8, -1, 0, 0 ),
264: 'COLUMN' => array( 9, -1, 0, 0 ),
265: 'NA' => array( 10, 0, 0, 0 ),
266: 'NPV' => array( 11, -1, 1, 0 ),
267: 'STDEV' => array( 12, -1, 0, 0 ),
268: 'DOLLAR' => array( 13, -1, 1, 0 ),
269: 'FIXED' => array( 14, -1, 1, 0 ),
270: 'SIN' => array( 15, 1, 1, 0 ),
271: 'COS' => array( 16, 1, 1, 0 ),
272: 'TAN' => array( 17, 1, 1, 0 ),
273: 'ATAN' => array( 18, 1, 1, 0 ),
274: 'PI' => array( 19, 0, 1, 0 ),
275: 'SQRT' => array( 20, 1, 1, 0 ),
276: 'EXP' => array( 21, 1, 1, 0 ),
277: 'LN' => array( 22, 1, 1, 0 ),
278: 'LOG10' => array( 23, 1, 1, 0 ),
279: 'ABS' => array( 24, 1, 1, 0 ),
280: 'INT' => array( 25, 1, 1, 0 ),
281: 'SIGN' => array( 26, 1, 1, 0 ),
282: 'ROUND' => array( 27, 2, 1, 0 ),
283: 'LOOKUP' => array( 28, -1, 0, 0 ),
284: 'INDEX' => array( 29, -1, 0, 1 ),
285: 'REPT' => array( 30, 2, 1, 0 ),
286: 'MID' => array( 31, 3, 1, 0 ),
287: 'LEN' => array( 32, 1, 1, 0 ),
288: 'VALUE' => array( 33, 1, 1, 0 ),
289: 'TRUE' => array( 34, 0, 1, 0 ),
290: 'FALSE' => array( 35, 0, 1, 0 ),
291: 'AND' => array( 36, -1, 0, 0 ),
292: 'OR' => array( 37, -1, 0, 0 ),
293: 'NOT' => array( 38, 1, 1, 0 ),
294: 'MOD' => array( 39, 2, 1, 0 ),
295: 'DCOUNT' => array( 40, 3, 0, 0 ),
296: 'DSUM' => array( 41, 3, 0, 0 ),
297: 'DAVERAGE' => array( 42, 3, 0, 0 ),
298: 'DMIN' => array( 43, 3, 0, 0 ),
299: 'DMAX' => array( 44, 3, 0, 0 ),
300: 'DSTDEV' => array( 45, 3, 0, 0 ),
301: 'VAR' => array( 46, -1, 0, 0 ),
302: 'DVAR' => array( 47, 3, 0, 0 ),
303: 'TEXT' => array( 48, 2, 1, 0 ),
304: 'LINEST' => array( 49, -1, 0, 0 ),
305: 'TREND' => array( 50, -1, 0, 0 ),
306: 'LOGEST' => array( 51, -1, 0, 0 ),
307: 'GROWTH' => array( 52, -1, 0, 0 ),
308: 'PV' => array( 56, -1, 1, 0 ),
309: 'FV' => array( 57, -1, 1, 0 ),
310: 'NPER' => array( 58, -1, 1, 0 ),
311: 'PMT' => array( 59, -1, 1, 0 ),
312: 'RATE' => array( 60, -1, 1, 0 ),
313: 'MIRR' => array( 61, 3, 0, 0 ),
314: 'IRR' => array( 62, -1, 0, 0 ),
315: 'RAND' => array( 63, 0, 1, 1 ),
316: 'MATCH' => array( 64, -1, 0, 0 ),
317: 'DATE' => array( 65, 3, 1, 0 ),
318: 'TIME' => array( 66, 3, 1, 0 ),
319: 'DAY' => array( 67, 1, 1, 0 ),
320: 'MONTH' => array( 68, 1, 1, 0 ),
321: 'YEAR' => array( 69, 1, 1, 0 ),
322: 'WEEKDAY' => array( 70, -1, 1, 0 ),
323: 'HOUR' => array( 71, 1, 1, 0 ),
324: 'MINUTE' => array( 72, 1, 1, 0 ),
325: 'SECOND' => array( 73, 1, 1, 0 ),
326: 'NOW' => array( 74, 0, 1, 1 ),
327: 'AREAS' => array( 75, 1, 0, 1 ),
328: 'ROWS' => array( 76, 1, 0, 1 ),
329: 'COLUMNS' => array( 77, 1, 0, 1 ),
330: 'OFFSET' => array( 78, -1, 0, 1 ),
331: 'SEARCH' => array( 82, -1, 1, 0 ),
332: 'TRANSPOSE' => array( 83, 1, 1, 0 ),
333: 'TYPE' => array( 86, 1, 1, 0 ),
334: 'ATAN2' => array( 97, 2, 1, 0 ),
335: 'ASIN' => array( 98, 1, 1, 0 ),
336: 'ACOS' => array( 99, 1, 1, 0 ),
337: 'CHOOSE' => array( 100, -1, 1, 0 ),
338: 'HLOOKUP' => array( 101, -1, 0, 0 ),
339: 'VLOOKUP' => array( 102, -1, 0, 0 ),
340: 'ISREF' => array( 105, 1, 0, 0 ),
341: 'LOG' => array( 109, -1, 1, 0 ),
342: 'CHAR' => array( 111, 1, 1, 0 ),
343: 'LOWER' => array( 112, 1, 1, 0 ),
344: 'UPPER' => array( 113, 1, 1, 0 ),
345: 'PROPER' => array( 114, 1, 1, 0 ),
346: 'LEFT' => array( 115, -1, 1, 0 ),
347: 'RIGHT' => array( 116, -1, 1, 0 ),
348: 'EXACT' => array( 117, 2, 1, 0 ),
349: 'TRIM' => array( 118, 1, 1, 0 ),
350: 'REPLACE' => array( 119, 4, 1, 0 ),
351: 'SUBSTITUTE' => array( 120, -1, 1, 0 ),
352: 'CODE' => array( 121, 1, 1, 0 ),
353: 'FIND' => array( 124, -1, 1, 0 ),
354: 'CELL' => array( 125, -1, 0, 1 ),
355: 'ISERR' => array( 126, 1, 1, 0 ),
356: 'ISTEXT' => array( 127, 1, 1, 0 ),
357: 'ISNUMBER' => array( 128, 1, 1, 0 ),
358: 'ISBLANK' => array( 129, 1, 1, 0 ),
359: 'T' => array( 130, 1, 0, 0 ),
360: 'N' => array( 131, 1, 0, 0 ),
361: 'DATEVALUE' => array( 140, 1, 1, 0 ),
362: 'TIMEVALUE' => array( 141, 1, 1, 0 ),
363: 'SLN' => array( 142, 3, 1, 0 ),
364: 'SYD' => array( 143, 4, 1, 0 ),
365: 'DDB' => array( 144, -1, 1, 0 ),
366: 'INDIRECT' => array( 148, -1, 1, 1 ),
367: 'CALL' => array( 150, -1, 1, 0 ),
368: 'CLEAN' => array( 162, 1, 1, 0 ),
369: 'MDETERM' => array( 163, 1, 2, 0 ),
370: 'MINVERSE' => array( 164, 1, 2, 0 ),
371: 'MMULT' => array( 165, 2, 2, 0 ),
372: 'IPMT' => array( 167, -1, 1, 0 ),
373: 'PPMT' => array( 168, -1, 1, 0 ),
374: 'COUNTA' => array( 169, -1, 0, 0 ),
375: 'PRODUCT' => array( 183, -1, 0, 0 ),
376: 'FACT' => array( 184, 1, 1, 0 ),
377: 'DPRODUCT' => array( 189, 3, 0, 0 ),
378: 'ISNONTEXT' => array( 190, 1, 1, 0 ),
379: 'STDEVP' => array( 193, -1, 0, 0 ),
380: 'VARP' => array( 194, -1, 0, 0 ),
381: 'DSTDEVP' => array( 195, 3, 0, 0 ),
382: 'DVARP' => array( 196, 3, 0, 0 ),
383: 'TRUNC' => array( 197, -1, 1, 0 ),
384: 'ISLOGICAL' => array( 198, 1, 1, 0 ),
385: 'DCOUNTA' => array( 199, 3, 0, 0 ),
386: 'USDOLLAR' => array( 204, -1, 1, 0 ),
387: 'FINDB' => array( 205, -1, 1, 0 ),
388: 'SEARCHB' => array( 206, -1, 1, 0 ),
389: 'REPLACEB' => array( 207, 4, 1, 0 ),
390: 'LEFTB' => array( 208, -1, 1, 0 ),
391: 'RIGHTB' => array( 209, -1, 1, 0 ),
392: 'MIDB' => array( 210, 3, 1, 0 ),
393: 'LENB' => array( 211, 1, 1, 0 ),
394: 'ROUNDUP' => array( 212, 2, 1, 0 ),
395: 'ROUNDDOWN' => array( 213, 2, 1, 0 ),
396: 'ASC' => array( 214, 1, 1, 0 ),
397: 'DBCS' => array( 215, 1, 1, 0 ),
398: 'RANK' => array( 216, -1, 0, 0 ),
399: 'ADDRESS' => array( 219, -1, 1, 0 ),
400: 'DAYS360' => array( 220, -1, 1, 0 ),
401: 'TODAY' => array( 221, 0, 1, 1 ),
402: 'VDB' => array( 222, -1, 1, 0 ),
403: 'MEDIAN' => array( 227, -1, 0, 0 ),
404: 'SUMPRODUCT' => array( 228, -1, 2, 0 ),
405: 'SINH' => array( 229, 1, 1, 0 ),
406: 'COSH' => array( 230, 1, 1, 0 ),
407: 'TANH' => array( 231, 1, 1, 0 ),
408: 'ASINH' => array( 232, 1, 1, 0 ),
409: 'ACOSH' => array( 233, 1, 1, 0 ),
410: 'ATANH' => array( 234, 1, 1, 0 ),
411: 'DGET' => array( 235, 3, 0, 0 ),
412: 'INFO' => array( 244, 1, 1, 1 ),
413: 'DB' => array( 247, -1, 1, 0 ),
414: 'FREQUENCY' => array( 252, 2, 0, 0 ),
415: 'ERROR.TYPE' => array( 261, 1, 1, 0 ),
416: 'REGISTER.ID' => array( 267, -1, 1, 0 ),
417: 'AVEDEV' => array( 269, -1, 0, 0 ),
418: 'BETADIST' => array( 270, -1, 1, 0 ),
419: 'GAMMALN' => array( 271, 1, 1, 0 ),
420: 'BETAINV' => array( 272, -1, 1, 0 ),
421: 'BINOMDIST' => array( 273, 4, 1, 0 ),
422: 'CHIDIST' => array( 274, 2, 1, 0 ),
423: 'CHIINV' => array( 275, 2, 1, 0 ),
424: 'COMBIN' => array( 276, 2, 1, 0 ),
425: 'CONFIDENCE' => array( 277, 3, 1, 0 ),
426: 'CRITBINOM' => array( 278, 3, 1, 0 ),
427: 'EVEN' => array( 279, 1, 1, 0 ),
428: 'EXPONDIST' => array( 280, 3, 1, 0 ),
429: 'FDIST' => array( 281, 3, 1, 0 ),
430: 'FINV' => array( 282, 3, 1, 0 ),
431: 'FISHER' => array( 283, 1, 1, 0 ),
432: 'FISHERINV' => array( 284, 1, 1, 0 ),
433: 'FLOOR' => array( 285, 2, 1, 0 ),
434: 'GAMMADIST' => array( 286, 4, 1, 0 ),
435: 'GAMMAINV' => array( 287, 3, 1, 0 ),
436: 'CEILING' => array( 288, 2, 1, 0 ),
437: 'HYPGEOMDIST' => array( 289, 4, 1, 0 ),
438: 'LOGNORMDIST' => array( 290, 3, 1, 0 ),
439: 'LOGINV' => array( 291, 3, 1, 0 ),
440: 'NEGBINOMDIST' => array( 292, 3, 1, 0 ),
441: 'NORMDIST' => array( 293, 4, 1, 0 ),
442: 'NORMSDIST' => array( 294, 1, 1, 0 ),
443: 'NORMINV' => array( 295, 3, 1, 0 ),
444: 'NORMSINV' => array( 296, 1, 1, 0 ),
445: 'STANDARDIZE' => array( 297, 3, 1, 0 ),
446: 'ODD' => array( 298, 1, 1, 0 ),
447: 'PERMUT' => array( 299, 2, 1, 0 ),
448: 'POISSON' => array( 300, 3, 1, 0 ),
449: 'TDIST' => array( 301, 3, 1, 0 ),
450: 'WEIBULL' => array( 302, 4, 1, 0 ),
451: 'SUMXMY2' => array( 303, 2, 2, 0 ),
452: 'SUMX2MY2' => array( 304, 2, 2, 0 ),
453: 'SUMX2PY2' => array( 305, 2, 2, 0 ),
454: 'CHITEST' => array( 306, 2, 2, 0 ),
455: 'CORREL' => array( 307, 2, 2, 0 ),
456: 'COVAR' => array( 308, 2, 2, 0 ),
457: 'FORECAST' => array( 309, 3, 2, 0 ),
458: 'FTEST' => array( 310, 2, 2, 0 ),
459: 'INTERCEPT' => array( 311, 2, 2, 0 ),
460: 'PEARSON' => array( 312, 2, 2, 0 ),
461: 'RSQ' => array( 313, 2, 2, 0 ),
462: 'STEYX' => array( 314, 2, 2, 0 ),
463: 'SLOPE' => array( 315, 2, 2, 0 ),
464: 'TTEST' => array( 316, 4, 2, 0 ),
465: 'PROB' => array( 317, -1, 2, 0 ),
466: 'DEVSQ' => array( 318, -1, 0, 0 ),
467: 'GEOMEAN' => array( 319, -1, 0, 0 ),
468: 'HARMEAN' => array( 320, -1, 0, 0 ),
469: 'SUMSQ' => array( 321, -1, 0, 0 ),
470: 'KURT' => array( 322, -1, 0, 0 ),
471: 'SKEW' => array( 323, -1, 0, 0 ),
472: 'ZTEST' => array( 324, -1, 0, 0 ),
473: 'LARGE' => array( 325, 2, 0, 0 ),
474: 'SMALL' => array( 326, 2, 0, 0 ),
475: 'QUARTILE' => array( 327, 2, 0, 0 ),
476: 'PERCENTILE' => array( 328, 2, 0, 0 ),
477: 'PERCENTRANK' => array( 329, -1, 0, 0 ),
478: 'MODE' => array( 330, -1, 2, 0 ),
479: 'TRIMMEAN' => array( 331, 2, 0, 0 ),
480: 'TINV' => array( 332, 2, 1, 0 ),
481: 'CONCATENATE' => array( 336, -1, 1, 0 ),
482: 'POWER' => array( 337, 2, 1, 0 ),
483: 'RADIANS' => array( 342, 1, 1, 0 ),
484: 'DEGREES' => array( 343, 1, 1, 0 ),
485: 'SUBTOTAL' => array( 344, -1, 0, 0 ),
486: 'SUMIF' => array( 345, -1, 0, 0 ),
487: 'COUNTIF' => array( 346, 2, 0, 0 ),
488: 'COUNTBLANK' => array( 347, 1, 0, 0 ),
489: 'ISPMT' => array( 350, 4, 1, 0 ),
490: 'DATEDIF' => array( 351, 3, 1, 0 ),
491: 'DATESTRING' => array( 352, 1, 1, 0 ),
492: 'NUMBERSTRING' => array( 353, 2, 1, 0 ),
493: 'ROMAN' => array( 354, -1, 1, 0 ),
494: 'GETPIVOTDATA' => array( 358, -1, 0, 0 ),
495: 'HYPERLINK' => array( 359, -1, 1, 0 ),
496: 'PHONETIC' => array( 360, 1, 0, 0 ),
497: 'AVERAGEA' => array( 361, -1, 0, 0 ),
498: 'MAXA' => array( 362, -1, 0, 0 ),
499: 'MINA' => array( 363, -1, 0, 0 ),
500: 'STDEVPA' => array( 364, -1, 0, 0 ),
501: 'VARPA' => array( 365, -1, 0, 0 ),
502: 'STDEVA' => array( 366, -1, 0, 0 ),
503: 'VARA' => array( 367, -1, 0, 0 ),
504: 'BAHTTEXT' => array( 368, 1, 0, 0 ),
505: );
506: }
507:
508: 509: 510: 511: 512: 513: 514:
515: function _convert($token)
516: {
517: if (preg_match("/\"([^\"]|\"\"){0,255}\"/", $token)) {
518: return $this->_convertString($token);
519:
520: } elseif (is_numeric($token)) {
521: return $this->_convertNumber($token);
522:
523:
524: } elseif (preg_match('/^\$?([A-Ia-i]?[A-Za-z])\$?(\d+)$/',$token)) {
525: return $this->_convertRef2d($token);
526:
527:
528: } elseif (preg_match("/^" . self::REGEX_SHEET_TITLE_UNQUOTED . "(\:" . self::REGEX_SHEET_TITLE_UNQUOTED . ")?\!\\$?[A-Ia-i]?[A-Za-z]\\$?(\d+)$/u",$token)) {
529: return $this->_convertRef3d($token);
530:
531:
532: } elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . "(\:" . self::REGEX_SHEET_TITLE_QUOTED . ")?'\!\\$?[A-Ia-i]?[A-Za-z]\\$?(\d+)$/u",$token)) {
533: return $this->_convertRef3d($token);
534:
535:
536: } elseif (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)\:(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)$/', $token)) {
537: return $this->_convertRange2d($token);
538:
539:
540: } elseif (preg_match("/^" . self::REGEX_SHEET_TITLE_UNQUOTED . "(\:" . self::REGEX_SHEET_TITLE_UNQUOTED . ")?\!\\$?([A-Ia-i]?[A-Za-z])?\\$?(\d+)\:\\$?([A-Ia-i]?[A-Za-z])?\\$?(\d+)$/u",$token)) {
541: return $this->_convertRange3d($token);
542:
543:
544: } elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . "(\:" . self::REGEX_SHEET_TITLE_QUOTED . ")?'\!\\$?([A-Ia-i]?[A-Za-z])?\\$?(\d+)\:\\$?([A-Ia-i]?[A-Za-z])?\\$?(\d+)$/u",$token)) {
545: return $this->_convertRange3d($token);
546:
547:
548: } elseif (isset($this->ptg[$token])) {
549: return pack("C", $this->ptg[$token]);
550:
551:
552: } elseif (preg_match("/^#[A-Z0\/]{3,5}[!?]{1}$/", $token) or $token == '#N/A') {
553: return $this->_convertError($token);
554:
555:
556: 557: 558: 559:
560:
561:
562: } elseif ($token == 'arg') {
563: return '';
564: }
565:
566:
567: throw new PHPExcel_Writer_Exception("Unknown token $token");
568: }
569:
570: 571: 572: 573: 574: 575:
576: function _convertNumber($num)
577: {
578:
579: if ((preg_match("/^\d+$/", $num)) and ($num <= 65535)) {
580: return pack("Cv", $this->ptg['ptgInt'], $num);
581: } else {
582: if (PHPExcel_Writer_Excel5_BIFFwriter::getByteOrder()) {
583: $num = strrev($num);
584: }
585: return pack("Cd", $this->ptg['ptgNum'], $num);
586: }
587: }
588:
589: 590: 591: 592: 593: 594: 595:
596: function _convertString($string)
597: {
598:
599: $string = substr($string, 1, strlen($string) - 2);
600: if (strlen($string) > 255) {
601: throw new PHPExcel_Writer_Exception("String is too long");
602: }
603:
604: return pack('C', $this->ptg['ptgStr']) . PHPExcel_Shared_String::UTF8toBIFF8UnicodeShort($string);
605: }
606:
607: 608: 609: 610: 611: 612: 613: 614: 615:
616: function _convertFunction($token, $num_args)
617: {
618: $args = $this->_functions[$token][1];
619:
620:
621:
622: if ($args >= 0) {
623: return pack("Cv", $this->ptg['ptgFuncV'], $this->_functions[$token][0]);
624: }
625:
626: if ($args == -1) {
627: return pack("CCv", $this->ptg['ptgFuncVarV'], $num_args, $this->_functions[$token][0]);
628: }
629: }
630:
631: 632: 633: 634: 635: 636: 637:
638: function _convertRange2d($range, $class=0)
639: {
640:
641:
642:
643: if (preg_match('/^(\$)?([A-Ia-i]?[A-Za-z])(\$)?(\d+)\:(\$)?([A-Ia-i]?[A-Za-z])(\$)?(\d+)$/', $range)) {
644: list($cell1, $cell2) = explode(':', $range);
645: } else {
646:
647: throw new PHPExcel_Writer_Exception("Unknown range separator");
648: }
649:
650:
651: list($row1, $col1) = $this->_cellToPackedRowcol($cell1);
652: list($row2, $col2) = $this->_cellToPackedRowcol($cell2);
653:
654:
655: if ($class == 0) {
656: $ptgArea = pack("C", $this->ptg['ptgArea']);
657: } elseif ($class == 1) {
658: $ptgArea = pack("C", $this->ptg['ptgAreaV']);
659: } elseif ($class == 2) {
660: $ptgArea = pack("C", $this->ptg['ptgAreaA']);
661: } else {
662:
663: throw new PHPExcel_Writer_Exception("Unknown class $class");
664: }
665: return $ptgArea . $row1 . $row2 . $col1. $col2;
666: }
667:
668: 669: 670: 671: 672: 673: 674: 675:
676: function _convertRange3d($token)
677: {
678:
679:
680:
681: list($ext_ref, $range) = explode('!', $token);
682:
683:
684: $ext_ref = $this->_getRefIndex($ext_ref);
685:
686:
687: list($cell1, $cell2) = explode(':', $range);
688:
689:
690: if (preg_match("/^(\\$)?[A-Ia-i]?[A-Za-z](\\$)?(\d+)$/", $cell1)) {
691: list($row1, $col1) = $this->_cellToPackedRowcol($cell1);
692: list($row2, $col2) = $this->_cellToPackedRowcol($cell2);
693: } else {
694: list($row1, $col1, $row2, $col2) = $this->_rangeToPackedRange($cell1.':'.$cell2);
695: }
696:
697:
698:
699: $ptgArea = pack("C", $this->ptg['ptgArea3d']);
700:
701:
702:
703:
704:
705:
706:
707:
708: return $ptgArea . $ext_ref . $row1 . $row2 . $col1. $col2;
709: }
710:
711: 712: 713: 714: 715: 716: 717:
718: function _convertRef2d($cell)
719: {
720:
721:
722:
723: $cell_array = $this->_cellToPackedRowcol($cell);
724: list($row, $col) = $cell_array;
725:
726:
727:
728:
729:
730:
731:
732: $ptgRef = pack("C", $this->ptg['ptgRefA']);
733:
734:
735:
736:
737: return $ptgRef.$row.$col;
738: }
739:
740: 741: 742: 743: 744: 745: 746: 747:
748: function _convertRef3d($cell)
749: {
750:
751:
752:
753: list($ext_ref, $cell) = explode('!', $cell);
754:
755:
756: $ext_ref = $this->_getRefIndex($ext_ref);
757:
758:
759: list($row, $col) = $this->_cellToPackedRowcol($cell);
760:
761:
762:
763:
764:
765:
766:
767: $ptgRef = pack("C", $this->ptg['ptgRef3dA']);
768:
769:
770:
771:
772: return $ptgRef . $ext_ref. $row . $col;
773: }
774:
775: 776: 777: 778: 779: 780: 781:
782: function _convertError($errorCode)
783: {
784: switch ($errorCode) {
785: case '#NULL!': return pack("C", 0x00);
786: case '#DIV/0!': return pack("C", 0x07);
787: case '#VALUE!': return pack("C", 0x0F);
788: case '#REF!': return pack("C", 0x17);
789: case '#NAME?': return pack("C", 0x1D);
790: case '#NUM!': return pack("C", 0x24);
791: case '#N/A': return pack("C", 0x2A);
792: }
793: return pack("C", 0xFF);
794: }
795:
796: 797: 798: 799: 800: 801: 802: 803:
804: function _packExtRef($ext_ref)
805: {
806: $ext_ref = preg_replace("/^'/", '', $ext_ref);
807: $ext_ref = preg_replace("/'$/", '', $ext_ref);
808:
809:
810: if (preg_match("/:/", $ext_ref)) {
811: list($sheet_name1, $sheet_name2) = explode(':', $ext_ref);
812:
813: $sheet1 = $this->_getSheetIndex($sheet_name1);
814: if ($sheet1 == -1) {
815: throw new PHPExcel_Writer_Exception("Unknown sheet name $sheet_name1 in formula");
816: }
817: $sheet2 = $this->_getSheetIndex($sheet_name2);
818: if ($sheet2 == -1) {
819: throw new PHPExcel_Writer_Exception("Unknown sheet name $sheet_name2 in formula");
820: }
821:
822:
823: if ($sheet1 > $sheet2) {
824: list($sheet1, $sheet2) = array($sheet2, $sheet1);
825: }
826: } else {
827: $sheet1 = $this->_getSheetIndex($ext_ref);
828: if ($sheet1 == -1) {
829: throw new PHPExcel_Writer_Exception("Unknown sheet name $ext_ref in formula");
830: }
831: $sheet2 = $sheet1;
832: }
833:
834:
835: $offset = -1 - $sheet1;
836:
837: return pack('vdvv', $offset, 0x00, $sheet1, $sheet2);
838: }
839:
840: 841: 842: 843: 844: 845: 846: 847: 848:
849: function _getRefIndex($ext_ref)
850: {
851: $ext_ref = preg_replace("/^'/", '', $ext_ref);
852: $ext_ref = preg_replace("/'$/", '', $ext_ref);
853: $ext_ref = str_replace('\'\'', '\'', $ext_ref);
854:
855:
856: if (preg_match("/:/", $ext_ref)) {
857: list($sheet_name1, $sheet_name2) = explode(':', $ext_ref);
858:
859: $sheet1 = $this->_getSheetIndex($sheet_name1);
860: if ($sheet1 == -1) {
861: throw new PHPExcel_Writer_Exception("Unknown sheet name $sheet_name1 in formula");
862: }
863: $sheet2 = $this->_getSheetIndex($sheet_name2);
864: if ($sheet2 == -1) {
865: throw new PHPExcel_Writer_Exception("Unknown sheet name $sheet_name2 in formula");
866: }
867:
868:
869: if ($sheet1 > $sheet2) {
870: list($sheet1, $sheet2) = array($sheet2, $sheet1);
871: }
872: } else {
873: $sheet1 = $this->_getSheetIndex($ext_ref);
874: if ($sheet1 == -1) {
875: throw new PHPExcel_Writer_Exception("Unknown sheet name $ext_ref in formula");
876: }
877: $sheet2 = $sheet1;
878: }
879:
880:
881: $supbook_index = 0x00;
882: $ref = pack('vvv', $supbook_index, $sheet1, $sheet2);
883: $total_references = count($this->_references);
884: $index = -1;
885: for ($i = 0; $i < $total_references; ++$i) {
886: if ($ref == $this->_references[$i]) {
887: $index = $i;
888: break;
889: }
890: }
891:
892: if ($index == -1) {
893: $this->_references[$total_references] = $ref;
894: $index = $total_references;
895: }
896:
897: return pack('v', $index);
898: }
899:
900: 901: 902: 903: 904: 905: 906: 907: 908:
909: function _getSheetIndex($sheet_name)
910: {
911: if (!isset($this->_ext_sheets[$sheet_name])) {
912: return -1;
913: } else {
914: return $this->_ext_sheets[$sheet_name];
915: }
916: }
917:
918: 919: 920: 921: 922: 923: 924: 925: 926: 927:
928: function setExtSheet($name, $index)
929: {
930: $this->_ext_sheets[$name] = $index;
931: }
932:
933: 934: 935: 936: 937: 938: 939:
940: function _cellToPackedRowcol($cell)
941: {
942: $cell = strtoupper($cell);
943: list($row, $col, $row_rel, $col_rel) = $this->_cellToRowcol($cell);
944: if ($col >= 256) {
945: throw new PHPExcel_Writer_Exception("Column in: $cell greater than 255");
946: }
947: if ($row >= 65536) {
948: throw new PHPExcel_Writer_Exception("Row in: $cell greater than 65536 ");
949: }
950:
951:
952: $col |= $col_rel << 14;
953: $col |= $row_rel << 15;
954: $col = pack('v', $col);
955:
956: $row = pack('v', $row);
957:
958: return array($row, $col);
959: }
960:
961: 962: 963: 964: 965: 966: 967: 968:
969: function _rangeToPackedRange($range)
970: {
971: preg_match('/(\$)?(\d+)\:(\$)?(\d+)/', $range, $match);
972:
973: $row1_rel = empty($match[1]) ? 1 : 0;
974: $row1 = $match[2];
975: $row2_rel = empty($match[3]) ? 1 : 0;
976: $row2 = $match[4];
977:
978: --$row1;
979: --$row2;
980:
981: $col1 = 0;
982: $col2 = 65535;
983:
984:
985: if (($row1 >= 65536) or ($row2 >= 65536)) {
986: throw new PHPExcel_Writer_Exception("Row in: $range greater than 65536 ");
987: }
988:
989:
990: $col1 |= $row1_rel << 15;
991: $col2 |= $row2_rel << 15;
992: $col1 = pack('v', $col1);
993: $col2 = pack('v', $col2);
994:
995: $row1 = pack('v', $row1);
996: $row2 = pack('v', $row2);
997:
998: return array($row1, $col1, $row2, $col2);
999: }
1000:
1001: 1002: 1003: 1004: 1005: 1006: 1007: 1008: 1009:
1010: function _cellToRowcol($cell)
1011: {
1012: preg_match('/(\$)?([A-I]?[A-Z])(\$)?(\d+)/',$cell,$match);
1013:
1014: $col_rel = empty($match[1]) ? 1 : 0;
1015: $col_ref = $match[2];
1016: $row_rel = empty($match[3]) ? 1 : 0;
1017: $row = $match[4];
1018:
1019:
1020: $expn = strlen($col_ref) - 1;
1021: $col = 0;
1022: $col_ref_length = strlen($col_ref);
1023: for ($i = 0; $i < $col_ref_length; ++$i) {
1024: $col += (ord($col_ref{$i}) - 64) * pow(26, $expn);
1025: --$expn;
1026: }
1027:
1028:
1029: --$row;
1030: --$col;
1031:
1032: return array($row, $col, $row_rel, $col_rel);
1033: }
1034:
1035: 1036: 1037: 1038: 1039:
1040: function _advance()
1041: {
1042: $i = $this->_current_char;
1043: $formula_length = strlen($this->_formula);
1044:
1045: if ($i < $formula_length) {
1046: while ($this->_formula{$i} == " ") {
1047: ++$i;
1048: }
1049:
1050: if ($i < ($formula_length - 1)) {
1051: $this->_lookahead = $this->_formula{$i+1};
1052: }
1053: $token = '';
1054: }
1055:
1056: while ($i < $formula_length) {
1057: $token .= $this->_formula{$i};
1058:
1059: if ($i < ($formula_length - 1)) {
1060: $this->_lookahead = $this->_formula{$i+1};
1061: } else {
1062: $this->_lookahead = '';
1063: }
1064:
1065: if ($this->_match($token) != '') {
1066:
1067:
1068:
1069: $this->_current_char = $i + 1;
1070: $this->_current_token = $token;
1071: return 1;
1072: }
1073:
1074: if ($i < ($formula_length - 2)) {
1075: $this->_lookahead = $this->_formula{$i+2};
1076: } else {
1077: $this->_lookahead = '';
1078: }
1079: ++$i;
1080: }
1081:
1082: }
1083:
1084: 1085: 1086: 1087: 1088: 1089: 1090:
1091: function _match($token)
1092: {
1093: switch($token) {
1094: case "+":
1095: case "-":
1096: case "*":
1097: case "/":
1098: case "(":
1099: case ")":
1100: case ",":
1101: case ";":
1102: case ">=":
1103: case "<=":
1104: case "=":
1105: case "<>":
1106: case "^":
1107: case "&":
1108: case "%":
1109: return $token;
1110: break;
1111: case ">":
1112: if ($this->_lookahead == '=') {
1113: break;
1114: }
1115: return $token;
1116: break;
1117: case "<":
1118:
1119: if (($this->_lookahead == '=') or ($this->_lookahead == '>')) {
1120: break;
1121: }
1122: return $token;
1123: break;
1124: default:
1125:
1126: if (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?[0-9]+$/',$token) and
1127: !preg_match("/[0-9]/",$this->_lookahead) and
1128: ($this->_lookahead != ':') and ($this->_lookahead != '.') and
1129: ($this->_lookahead != '!'))
1130: {
1131: return $token;
1132: }
1133:
1134: elseif (preg_match("/^" . self::REGEX_SHEET_TITLE_UNQUOTED . "(\:" . self::REGEX_SHEET_TITLE_UNQUOTED . ")?\!\\$?[A-Ia-i]?[A-Za-z]\\$?[0-9]+$/u",$token) and
1135: !preg_match("/[0-9]/",$this->_lookahead) and
1136: ($this->_lookahead != ':') and ($this->_lookahead != '.'))
1137: {
1138: return $token;
1139: }
1140:
1141: elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . "(\:" . self::REGEX_SHEET_TITLE_QUOTED . ")?'\!\\$?[A-Ia-i]?[A-Za-z]\\$?[0-9]+$/u",$token) and
1142: !preg_match("/[0-9]/",$this->_lookahead) and
1143: ($this->_lookahead != ':') and ($this->_lookahead != '.'))
1144: {
1145: return $token;
1146: }
1147:
1148: elseif (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+:(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+$/', $token) and
1149: !preg_match("/[0-9]/",$this->_lookahead))
1150: {
1151: return $token;
1152: }
1153:
1154: elseif (preg_match("/^" . self::REGEX_SHEET_TITLE_UNQUOTED . "(\:" . self::REGEX_SHEET_TITLE_UNQUOTED . ")?\!\\$?([A-Ia-i]?[A-Za-z])?\\$?[0-9]+:\\$?([A-Ia-i]?[A-Za-z])?\\$?[0-9]+$/u",$token) and
1155: !preg_match("/[0-9]/",$this->_lookahead))
1156: {
1157: return $token;
1158: }
1159:
1160: elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . "(\:" . self::REGEX_SHEET_TITLE_QUOTED . ")?'\!\\$?([A-Ia-i]?[A-Za-z])?\\$?[0-9]+:\\$?([A-Ia-i]?[A-Za-z])?\\$?[0-9]+$/u",$token) and
1161: !preg_match("/[0-9]/",$this->_lookahead))
1162: {
1163: return $token;
1164: }
1165:
1166: elseif (is_numeric($token) and
1167: (!is_numeric($token.$this->_lookahead) or ($this->_lookahead == '')) and
1168: ($this->_lookahead != '!') and ($this->_lookahead != ':'))
1169: {
1170: return $token;
1171: }
1172:
1173: elseif (preg_match("/\"([^\"]|\"\"){0,255}\"/",$token) and $this->_lookahead != '"' and (substr_count($token, '"')%2 == 0))
1174: {
1175: return $token;
1176: }
1177:
1178: elseif (preg_match("/^#[A-Z0\/]{3,5}[!?]{1}$/", $token) or $token == '#N/A')
1179: {
1180: return $token;
1181: }
1182:
1183: elseif (preg_match("/^[A-Z0-9\xc0-\xdc\.]+$/i",$token) and ($this->_lookahead == "("))
1184: {
1185: return $token;
1186: }
1187:
1188:
1189: elseif(substr($token,-1) == ')') {
1190: return $token;
1191: }
1192: return '';
1193: }
1194: }
1195:
1196: 1197: 1198: 1199: 1200: 1201: 1202: 1203:
1204: function parse($formula)
1205: {
1206: $this->_current_char = 0;
1207: $this->_formula = $formula;
1208: $this->_lookahead = isset($formula{1}) ? $formula{1} : '';
1209: $this->_advance();
1210: $this->_parse_tree = $this->_condition();
1211: return true;
1212: }
1213:
1214: 1215: 1216: 1217: 1218: 1219: 1220:
1221: function _condition()
1222: {
1223: $result = $this->_expression();
1224: if ($this->_current_token == "<") {
1225: $this->_advance();
1226: $result2 = $this->_expression();
1227: $result = $this->_createTree('ptgLT', $result, $result2);
1228: } elseif ($this->_current_token == ">") {
1229: $this->_advance();
1230: $result2 = $this->_expression();
1231: $result = $this->_createTree('ptgGT', $result, $result2);
1232: } elseif ($this->_current_token == "<=") {
1233: $this->_advance();
1234: $result2 = $this->_expression();
1235: $result = $this->_createTree('ptgLE', $result, $result2);
1236: } elseif ($this->_current_token == ">=") {
1237: $this->_advance();
1238: $result2 = $this->_expression();
1239: $result = $this->_createTree('ptgGE', $result, $result2);
1240: } elseif ($this->_current_token == "=") {
1241: $this->_advance();
1242: $result2 = $this->_expression();
1243: $result = $this->_createTree('ptgEQ', $result, $result2);
1244: } elseif ($this->_current_token == "<>") {
1245: $this->_advance();
1246: $result2 = $this->_expression();
1247: $result = $this->_createTree('ptgNE', $result, $result2);
1248: } elseif ($this->_current_token == "&") {
1249: $this->_advance();
1250: $result2 = $this->_expression();
1251: $result = $this->_createTree('ptgConcat', $result, $result2);
1252: }
1253: return $result;
1254: }
1255:
1256: 1257: 1258: 1259: 1260: 1261: 1262: 1263: 1264: 1265: 1266:
1267: function _expression()
1268: {
1269:
1270: if (preg_match("/\"([^\"]|\"\"){0,255}\"/", $this->_current_token)) {
1271: $tmp = str_replace('""', '"', $this->_current_token);
1272: if (($tmp == '"') || ($tmp == '')) $tmp = '""';
1273: $result = $this->_createTree($tmp, '', '');
1274: $this->_advance();
1275: return $result;
1276:
1277: } elseif (preg_match("/^#[A-Z0\/]{3,5}[!?]{1}$/", $this->_current_token) or $this->_current_token == '#N/A'){
1278: $result = $this->_createTree($this->_current_token, 'ptgErr', '');
1279: $this->_advance();
1280: return $result;
1281:
1282: } elseif ($this->_current_token == "-") {
1283:
1284: $this->_advance();
1285: $result2 = $this->_expression();
1286: $result = $this->_createTree('ptgUminus', $result2, '');
1287: return $result;
1288:
1289: } elseif ($this->_current_token == "+") {
1290:
1291: $this->_advance();
1292: $result2 = $this->_expression();
1293: $result = $this->_createTree('ptgUplus', $result2, '');
1294: return $result;
1295: }
1296: $result = $this->_term();
1297: while (($this->_current_token == "+") or
1298: ($this->_current_token == "-") or
1299: ($this->_current_token == "^")) {
1300:
1301: if ($this->_current_token == "+") {
1302: $this->_advance();
1303: $result2 = $this->_term();
1304: $result = $this->_createTree('ptgAdd', $result, $result2);
1305: } elseif ($this->_current_token == "-") {
1306: $this->_advance();
1307: $result2 = $this->_term();
1308: $result = $this->_createTree('ptgSub', $result, $result2);
1309: } else {
1310: $this->_advance();
1311: $result2 = $this->_term();
1312: $result = $this->_createTree('ptgPower', $result, $result2);
1313: }
1314: }
1315: return $result;
1316: }
1317:
1318: 1319: 1320: 1321: 1322: 1323: 1324: 1325:
1326: function _parenthesizedExpression()
1327: {
1328: $result = $this->_createTree('ptgParen', $this->_expression(), '');
1329: return $result;
1330: }
1331:
1332: 1333: 1334: 1335: 1336: 1337: 1338:
1339: function _term()
1340: {
1341: $result = $this->_fact();
1342: while (($this->_current_token == "*") or
1343: ($this->_current_token == "/")) {
1344:
1345: if ($this->_current_token == "*") {
1346: $this->_advance();
1347: $result2 = $this->_fact();
1348: $result = $this->_createTree('ptgMul', $result, $result2);
1349: } else {
1350: $this->_advance();
1351: $result2 = $this->_fact();
1352: $result = $this->_createTree('ptgDiv', $result, $result2);
1353: }
1354: }
1355: return $result;
1356: }
1357:
1358: 1359: 1360: 1361: 1362: 1363: 1364: 1365: 1366: 1367: 1368:
1369: function _fact()
1370: {
1371: if ($this->_current_token == "(") {
1372: $this->_advance();
1373: $result = $this->_parenthesizedExpression();
1374: if ($this->_current_token != ")") {
1375: throw new PHPExcel_Writer_Exception("')' token expected.");
1376: }
1377: $this->_advance();
1378: return $result;
1379: }
1380:
1381: if (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?[0-9]+$/',$this->_current_token))
1382: {
1383: $result = $this->_createTree($this->_current_token, '', '');
1384: $this->_advance();
1385: return $result;
1386: }
1387:
1388: elseif (preg_match("/^" . self::REGEX_SHEET_TITLE_UNQUOTED . "(\:" . self::REGEX_SHEET_TITLE_UNQUOTED . ")?\!\\$?[A-Ia-i]?[A-Za-z]\\$?[0-9]+$/u",$this->_current_token))
1389: {
1390: $result = $this->_createTree($this->_current_token, '', '');
1391: $this->_advance();
1392: return $result;
1393: }
1394:
1395: elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . "(\:" . self::REGEX_SHEET_TITLE_QUOTED . ")?'\!\\$?[A-Ia-i]?[A-Za-z]\\$?[0-9]+$/u",$this->_current_token))
1396: {
1397: $result = $this->_createTree($this->_current_token, '', '');
1398: $this->_advance();
1399: return $result;
1400: }
1401:
1402: elseif (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+:(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+$/',$this->_current_token) or
1403: preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+\.\.(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+$/',$this->_current_token))
1404: {
1405:
1406: $result = $this->_createTree($this->_current_token, '', '');
1407: $this->_advance();
1408: return $result;
1409: }
1410:
1411: elseif (preg_match("/^" . self::REGEX_SHEET_TITLE_UNQUOTED . "(\:" . self::REGEX_SHEET_TITLE_UNQUOTED . ")?\!\\$?([A-Ia-i]?[A-Za-z])?\\$?[0-9]+:\\$?([A-Ia-i]?[A-Za-z])?\\$?[0-9]+$/u",$this->_current_token))
1412: {
1413:
1414:
1415: $result = $this->_createTree($this->_current_token, '', '');
1416: $this->_advance();
1417: return $result;
1418: }
1419:
1420: elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . "(\:" . self::REGEX_SHEET_TITLE_QUOTED . ")?'\!\\$?([A-Ia-i]?[A-Za-z])?\\$?[0-9]+:\\$?([A-Ia-i]?[A-Za-z])?\\$?[0-9]+$/u",$this->_current_token))
1421: {
1422:
1423:
1424: $result = $this->_createTree($this->_current_token, '', '');
1425: $this->_advance();
1426: return $result;
1427: }
1428:
1429: elseif (is_numeric($this->_current_token))
1430: {
1431: if($this->_lookahead == '%'){
1432: $result = $this->_createTree('ptgPercent', $this->_current_token, '');
1433: } else {
1434: $result = $this->_createTree($this->_current_token, '', '');
1435: }
1436: $this->_advance();
1437: return $result;
1438: }
1439:
1440: elseif (preg_match("/^[A-Z0-9\xc0-\xdc\.]+$/i",$this->_current_token))
1441: {
1442: $result = $this->_func();
1443: return $result;
1444: }
1445: throw new PHPExcel_Writer_Exception("Syntax error: ".$this->_current_token.
1446: ", lookahead: ".$this->_lookahead.
1447: ", current char: ".$this->_current_char);
1448: }
1449:
1450: 1451: 1452: 1453: 1454: 1455: 1456:
1457: function _func()
1458: {
1459: $num_args = 0;
1460: $function = strtoupper($this->_current_token);
1461: $result = '';
1462: $this->_advance();
1463: $this->_advance();
1464: while ($this->_current_token != ')') {
1465:
1466: if ($num_args > 0) {
1467: if ($this->_current_token == "," or
1468: $this->_current_token == ";")
1469: {
1470: $this->_advance();
1471: } else {
1472: throw new PHPExcel_Writer_Exception("Syntax error: comma expected in ".
1473: "function $function, arg #{$num_args}");
1474: }
1475: $result2 = $this->_condition();
1476: $result = $this->_createTree('arg', $result, $result2);
1477: } else {
1478: $result2 = $this->_condition();
1479: $result = $this->_createTree('arg', '', $result2);
1480: }
1481: ++$num_args;
1482: }
1483: if (!isset($this->_functions[$function])) {
1484: throw new PHPExcel_Writer_Exception("Function $function() doesn't exist");
1485: }
1486: $args = $this->_functions[$function][1];
1487:
1488: if (($args >= 0) and ($args != $num_args)) {
1489: throw new PHPExcel_Writer_Exception("Incorrect number of arguments in function $function() ");
1490: }
1491:
1492: $result = $this->_createTree($function, $result, $num_args);
1493: $this->_advance();
1494: return $result;
1495: }
1496:
1497: 1498: 1499: 1500: 1501: 1502: 1503: 1504: 1505: 1506:
1507: function _createTree($value, $left, $right)
1508: {
1509: return array('value' => $value, 'left' => $left, 'right' => $right);
1510: }
1511:
1512: 1513: 1514: 1515: 1516: 1517: 1518: 1519: 1520: 1521: 1522: 1523: 1524: 1525: 1526: 1527: 1528: 1529: 1530: 1531: 1532: 1533: 1534: 1535: 1536: 1537: 1538:
1539: function toReversePolish($tree = array())
1540: {
1541: $polish = "";
1542: if (empty($tree)) {
1543: $tree = $this->_parse_tree;
1544: }
1545:
1546: if (is_array($tree['left'])) {
1547: $converted_tree = $this->toReversePolish($tree['left']);
1548: $polish .= $converted_tree;
1549: } elseif ($tree['left'] != '') {
1550: $converted_tree = $this->_convert($tree['left']);
1551: $polish .= $converted_tree;
1552: }
1553: if (is_array($tree['right'])) {
1554: $converted_tree = $this->toReversePolish($tree['right']);
1555: $polish .= $converted_tree;
1556: } elseif ($tree['right'] != '') {
1557: $converted_tree = $this->_convert($tree['right']);
1558: $polish .= $converted_tree;
1559: }
1560:
1561: if (preg_match("/^[A-Z0-9\xc0-\xdc\.]+$/",$tree['value']) and
1562: !preg_match('/^([A-Ia-i]?[A-Za-z])(\d+)$/',$tree['value']) and
1563: !preg_match("/^[A-Ia-i]?[A-Za-z](\d+)\.\.[A-Ia-i]?[A-Za-z](\d+)$/",$tree['value']) and
1564: !is_numeric($tree['value']) and
1565: !isset($this->ptg[$tree['value']]))
1566: {
1567:
1568: if ($tree['left'] != '') {
1569: $left_tree = $this->toReversePolish($tree['left']);
1570: } else {
1571: $left_tree = '';
1572: }
1573:
1574: return $left_tree.$this->_convertFunction($tree['value'], $tree['right']);
1575: } else {
1576: $converted_tree = $this->_convert($tree['value']);
1577: }
1578: $polish .= $converted_tree;
1579: return $polish;
1580: }
1581:
1582: }
1583: