diff options
Diffstat (limited to 'tablib/packages/xlrd/formula.py')
| -rw-r--r-- | tablib/packages/xlrd/formula.py | 2092 |
1 files changed, 0 insertions, 2092 deletions
diff --git a/tablib/packages/xlrd/formula.py b/tablib/packages/xlrd/formula.py deleted file mode 100644 index 4edbc29..0000000 --- a/tablib/packages/xlrd/formula.py +++ /dev/null @@ -1,2092 +0,0 @@ -# -*- coding: cp1252 -*- - -## -# Module for parsing/evaluating Microsoft Excel formulas. -# -# <p>Copyright © 2005-2009 Stephen John Machin, Lingfo Pty Ltd</p> -# <p>This module is part of the xlrd package, which is released under -# a BSD-style licence.</p> -## - -# No part of the content of this file was derived from the works of David Giffin. - -import copy -from struct import unpack -from timemachine import * -from biffh import unpack_unicode_update_pos, unpack_string_update_pos, \ - XLRDError, hex_char_dump, error_text_from_code, BaseObject - -__all__ = [ - 'oBOOL', 'oERR', 'oNUM', 'oREF', 'oREL', 'oSTRG', 'oUNK', - 'decompile_formula', - 'dump_formula', - 'evaluate_name_formula', - 'okind_dict', - 'rangename3d', 'rangename3drel', 'cellname', 'cellnameabs', 'colname', - ] - -# sztabN[opcode] -> the number of bytes to consume. -# -1 means variable -# -2 means this opcode not implemented in this version. -# Which N to use? Depends on biff_version; see szdict. -sztab0 = [-2, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -2, -1, 8, 4, 2, 2, 3, 9, 8, 2, 3, 8, 4, 7, 5, 5, 5, 2, 4, 7, 4, 7, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2, 3, -2, -2, -2, -2, -2, -2, -2] -sztab1 = [-2, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -2, -1, 11, 5, 2, 2, 3, 9, 9, 2, 3, 11, 4, 7, 7, 7, 7, 3, 4, 7, 4, 7, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2, 3, -2, -2, -2, -2, -2, -2, -2] -sztab2 = [-2, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -2, -1, 11, 5, 2, 2, 3, 9, 9, 3, 4, 11, 4, 7, 7, 7, 7, 3, 4, 7, 4, 7, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2] -sztab3 = [-2, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -2, -1, -2, -2, 2, 2, 3, 9, 9, 3, 4, 15, 4, 7, 7, 7, 7, 3, 4, 7, 4, 7, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2, -2, 25, 18, 21, 18, 21, -2, -2] -sztab4 = [-2, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -2, -2, 2, 2, 3, 9, 9, 3, 4, 5, 5, 9, 7, 7, 7, 3, 5, 9, 5, 9, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2, -2, 7, 7, 11, 7, 11, -2, -2] - -szdict = { - 20 : sztab0, - 30 : sztab1, - 40 : sztab2, - 45 : sztab2, - 50 : sztab3, - 70 : sztab3, - 80 : sztab4, - } - -# For debugging purposes ... the name for each opcode -# (without the prefix "t" used on OOo docs) -onames = ['Unk00', 'Exp', 'Tbl', 'Add', 'Sub', 'Mul', 'Div', 'Power', 'Concat', 'LT', 'LE', 'EQ', 'GE', 'GT', 'NE', 'Isect', 'List', 'Range', 'Uplus', 'Uminus', 'Percent', 'Paren', 'MissArg', 'Str', 'Extended', 'Attr', 'Sheet', 'EndSheet', 'Err', 'Bool', 'Int', 'Num', 'Array', 'Func', 'FuncVar', 'Name', 'Ref', 'Area', 'MemArea', 'MemErr', 'MemNoMem', 'MemFunc', 'RefErr', 'AreaErr', 'RefN', 'AreaN', 'MemAreaN', 'MemNoMemN', '', '', '', '', '', '', '', '', 'FuncCE', 'NameX', 'Ref3d', 'Area3d', 'RefErr3d', 'AreaErr3d', '', ''] - -func_defs = { - # index: (name, min#args, max#args, flags, #known_args, return_type, kargs) - 0 : ('COUNT', 0, 30, 0x04, 1, 'V', 'R'), - 1 : ('IF', 2, 3, 0x04, 3, 'V', 'VRR'), - 2 : ('ISNA', 1, 1, 0x02, 1, 'V', 'V'), - 3 : ('ISERROR', 1, 1, 0x02, 1, 'V', 'V'), - 4 : ('SUM', 0, 30, 0x04, 1, 'V', 'R'), - 5 : ('AVERAGE', 1, 30, 0x04, 1, 'V', 'R'), - 6 : ('MIN', 1, 30, 0x04, 1, 'V', 'R'), - 7 : ('MAX', 1, 30, 0x04, 1, 'V', 'R'), - 8 : ('ROW', 0, 1, 0x04, 1, 'V', 'R'), - 9 : ('COLUMN', 0, 1, 0x04, 1, 'V', 'R'), - 10 : ('NA', 0, 0, 0x02, 0, 'V', ''), - 11 : ('NPV', 2, 30, 0x04, 2, 'V', 'VR'), - 12 : ('STDEV', 1, 30, 0x04, 1, 'V', 'R'), - 13 : ('DOLLAR', 1, 2, 0x04, 1, 'V', 'V'), - 14 : ('FIXED', 2, 3, 0x04, 3, 'V', 'VVV'), - 15 : ('SIN', 1, 1, 0x02, 1, 'V', 'V'), - 16 : ('COS', 1, 1, 0x02, 1, 'V', 'V'), - 17 : ('TAN', 1, 1, 0x02, 1, 'V', 'V'), - 18 : ('ATAN', 1, 1, 0x02, 1, 'V', 'V'), - 19 : ('PI', 0, 0, 0x02, 0, 'V', ''), - 20 : ('SQRT', 1, 1, 0x02, 1, 'V', 'V'), - 21 : ('EXP', 1, 1, 0x02, 1, 'V', 'V'), - 22 : ('LN', 1, 1, 0x02, 1, 'V', 'V'), - 23 : ('LOG10', 1, 1, 0x02, 1, 'V', 'V'), - 24 : ('ABS', 1, 1, 0x02, 1, 'V', 'V'), - 25 : ('INT', 1, 1, 0x02, 1, 'V', 'V'), - 26 : ('SIGN', 1, 1, 0x02, 1, 'V', 'V'), - 27 : ('ROUND', 2, 2, 0x02, 2, 'V', 'VV'), - 28 : ('LOOKUP', 2, 3, 0x04, 2, 'V', 'VR'), - 29 : ('INDEX', 2, 4, 0x0c, 4, 'R', 'RVVV'), - 30 : ('REPT', 2, 2, 0x02, 2, 'V', 'VV'), - 31 : ('MID', 3, 3, 0x02, 3, 'V', 'VVV'), - 32 : ('LEN', 1, 1, 0x02, 1, 'V', 'V'), - 33 : ('VALUE', 1, 1, 0x02, 1, 'V', 'V'), - 34 : ('TRUE', 0, 0, 0x02, 0, 'V', ''), - 35 : ('FALSE', 0, 0, 0x02, 0, 'V', ''), - 36 : ('AND', 1, 30, 0x04, 1, 'V', 'R'), - 37 : ('OR', 1, 30, 0x04, 1, 'V', 'R'), - 38 : ('NOT', 1, 1, 0x02, 1, 'V', 'V'), - 39 : ('MOD', 2, 2, 0x02, 2, 'V', 'VV'), - 40 : ('DCOUNT', 3, 3, 0x02, 3, 'V', 'RRR'), - 41 : ('DSUM', 3, 3, 0x02, 3, 'V', 'RRR'), - 42 : ('DAVERAGE', 3, 3, 0x02, 3, 'V', 'RRR'), - 43 : ('DMIN', 3, 3, 0x02, 3, 'V', 'RRR'), - 44 : ('DMAX', 3, 3, 0x02, 3, 'V', 'RRR'), - 45 : ('DSTDEV', 3, 3, 0x02, 3, 'V', 'RRR'), - 46 : ('VAR', 1, 30, 0x04, 1, 'V', 'R'), - 47 : ('DVAR', 3, 3, 0x02, 3, 'V', 'RRR'), - 48 : ('TEXT', 2, 2, 0x02, 2, 'V', 'VV'), - 49 : ('LINEST', 1, 4, 0x04, 4, 'A', 'RRVV'), - 50 : ('TREND', 1, 4, 0x04, 4, 'A', 'RRRV'), - 51 : ('LOGEST', 1, 4, 0x04, 4, 'A', 'RRVV'), - 52 : ('GROWTH', 1, 4, 0x04, 4, 'A', 'RRRV'), - 56 : ('PV', 3, 5, 0x04, 5, 'V', 'VVVVV'), - 57 : ('FV', 3, 5, 0x04, 5, 'V', 'VVVVV'), - 58 : ('NPER', 3, 5, 0x04, 5, 'V', 'VVVVV'), - 59 : ('PMT', 3, 5, 0x04, 5, 'V', 'VVVVV'), - 60 : ('RATE', 3, 6, 0x04, 6, 'V', 'VVVVVV'), - 61 : ('MIRR', 3, 3, 0x02, 3, 'V', 'RVV'), - 62 : ('IRR', 1, 2, 0x04, 2, 'V', 'RV'), - 63 : ('RAND', 0, 0, 0x0a, 0, 'V', ''), - 64 : ('MATCH', 2, 3, 0x04, 3, 'V', 'VRR'), - 65 : ('DATE', 3, 3, 0x02, 3, 'V', 'VVV'), - 66 : ('TIME', 3, 3, 0x02, 3, 'V', 'VVV'), - 67 : ('DAY', 1, 1, 0x02, 1, 'V', 'V'), - 68 : ('MONTH', 1, 1, 0x02, 1, 'V', 'V'), - 69 : ('YEAR', 1, 1, 0x02, 1, 'V', 'V'), - 70 : ('WEEKDAY', 1, 2, 0x04, 2, 'V', 'VV'), - 71 : ('HOUR', 1, 1, 0x02, 1, 'V', 'V'), - 72 : ('MINUTE', 1, 1, 0x02, 1, 'V', 'V'), - 73 : ('SECOND', 1, 1, 0x02, 1, 'V', 'V'), - 74 : ('NOW', 0, 0, 0x0a, 0, 'V', ''), - 75 : ('AREAS', 1, 1, 0x02, 1, 'V', 'R'), - 76 : ('ROWS', 1, 1, 0x02, 1, 'V', 'R'), - 77 : ('COLUMNS', 1, 1, 0x02, 1, 'V', 'R'), - 78 : ('OFFSET', 3, 5, 0x04, 5, 'R', 'RVVVV'), - 82 : ('SEARCH', 2, 3, 0x04, 3, 'V', 'VVV'), - 83 : ('TRANSPOSE', 1, 1, 0x02, 1, 'A', 'A'), - 86 : ('TYPE', 1, 1, 0x02, 1, 'V', 'V'), - 92 : ('SERIESSUM', 4, 4, 0x02, 4, 'V', 'VVVA'), - 97 : ('ATAN2', 2, 2, 0x02, 2, 'V', 'VV'), - 98 : ('ASIN', 1, 1, 0x02, 1, 'V', 'V'), - 99 : ('ACOS', 1, 1, 0x02, 1, 'V', 'V'), - 100: ('CHOOSE', 2, 30, 0x04, 2, 'V', 'VR'), - 101: ('HLOOKUP', 3, 4, 0x04, 4, 'V', 'VRRV'), - 102: ('VLOOKUP', 3, 4, 0x04, 4, 'V', 'VRRV'), - 105: ('ISREF', 1, 1, 0x02, 1, 'V', 'R'), - 109: ('LOG', 1, 2, 0x04, 2, 'V', 'VV'), - 111: ('CHAR', 1, 1, 0x02, 1, 'V', 'V'), - 112: ('LOWER', 1, 1, 0x02, 1, 'V', 'V'), - 113: ('UPPER', 1, 1, 0x02, 1, 'V', 'V'), - 114: ('PROPER', 1, 1, 0x02, 1, 'V', 'V'), - 115: ('LEFT', 1, 2, 0x04, 2, 'V', 'VV'), - 116: ('RIGHT', 1, 2, 0x04, 2, 'V', 'VV'), - 117: ('EXACT', 2, 2, 0x02, 2, 'V', 'VV'), - 118: ('TRIM', 1, 1, 0x02, 1, 'V', 'V'), - 119: ('REPLACE', 4, 4, 0x02, 4, 'V', 'VVVV'), - 120: ('SUBSTITUTE', 3, 4, 0x04, 4, 'V', 'VVVV'), - 121: ('CODE', 1, 1, 0x02, 1, 'V', 'V'), - 124: ('FIND', 2, 3, 0x04, 3, 'V', 'VVV'), - 125: ('CELL', 1, 2, 0x0c, 2, 'V', 'VR'), - 126: ('ISERR', 1, 1, 0x02, 1, 'V', 'V'), - 127: ('ISTEXT', 1, 1, 0x02, 1, 'V', 'V'), - 128: ('ISNUMBER', 1, 1, 0x02, 1, 'V', 'V'), - 129: ('ISBLANK', 1, 1, 0x02, 1, 'V', 'V'), - 130: ('T', 1, 1, 0x02, 1, 'V', 'R'), - 131: ('N', 1, 1, 0x02, 1, 'V', 'R'), - 140: ('DATEVALUE', 1, 1, 0x02, 1, 'V', 'V'), - 141: ('TIMEVALUE', 1, 1, 0x02, 1, 'V', 'V'), - 142: ('SLN', 3, 3, 0x02, 3, 'V', 'VVV'), - 143: ('SYD', 4, 4, 0x02, 4, 'V', 'VVVV'), - 144: ('DDB', 4, 5, 0x04, 5, 'V', 'VVVVV'), - 148: ('INDIRECT', 1, 2, 0x0c, 2, 'R', 'VV'), - 162: ('CLEAN', 1, 1, 0x02, 1, 'V', 'V'), - 163: ('MDETERM', 1, 1, 0x02, 1, 'V', 'A'), - 164: ('MINVERSE', 1, 1, 0x02, 1, 'A', 'A'), - 165: ('MMULT', 2, 2, 0x02, 2, 'A', 'AA'), - 167: ('IPMT', 4, 6, 0x04, 6, 'V', 'VVVVVV'), - 168: ('PPMT', 4, 6, 0x04, 6, 'V', 'VVVVVV'), - 169: ('COUNTA', 0, 30, 0x04, 1, 'V', 'R'), - 183: ('PRODUCT', 0, 30, 0x04, 1, 'V', 'R'), - 184: ('FACT', 1, 1, 0x02, 1, 'V', 'V'), - 189: ('DPRODUCT', 3, 3, 0x02, 3, 'V', 'RRR'), - 190: ('ISNONTEXT', 1, 1, 0x02, 1, 'V', 'V'), - 193: ('STDEVP', 1, 30, 0x04, 1, 'V', 'R'), - 194: ('VARP', 1, 30, 0x04, 1, 'V', 'R'), - 195: ('DSTDEVP', 3, 3, 0x02, 3, 'V', 'RRR'), - 196: ('DVARP', 3, 3, 0x02, 3, 'V', 'RRR'), - 197: ('TRUNC', 1, 2, 0x04, 2, 'V', 'VV'), - 198: ('ISLOGICAL', 1, 1, 0x02, 1, 'V', 'V'), - 199: ('DCOUNTA', 3, 3, 0x02, 3, 'V', 'RRR'), - 204: ('USDOLLAR', 1, 2, 0x04, 2, 'V', 'VV'), - 205: ('FINDB', 2, 3, 0x04, 3, 'V', 'VVV'), - 206: ('SEARCHB', 2, 3, 0x04, 3, 'V', 'VVV'), - 207: ('REPLACEB', 4, 4, 0x02, 4, 'V', 'VVVV'), - 208: ('LEFTB', 1, 2, 0x04, 2, 'V', 'VV'), - 209: ('RIGHTB', 1, 2, 0x04, 2, 'V', 'VV'), - 210: ('MIDB', 3, 3, 0x02, 3, 'V', 'VVV'), - 211: ('LENB', 1, 1, 0x02, 1, 'V', 'V'), - 212: ('ROUNDUP', 2, 2, 0x02, 2, 'V', 'VV'), - 213: ('ROUNDDOWN', 2, 2, 0x02, 2, 'V', 'VV'), - 214: ('ASC', 1, 1, 0x02, 1, 'V', 'V'), - 215: ('DBCS', 1, 1, 0x02, 1, 'V', 'V'), - 216: ('RANK', 2, 3, 0x04, 3, 'V', 'VRV'), - 219: ('ADDRESS', 2, 5, 0x04, 5, 'V', 'VVVVV'), - 220: ('DAYS360', 2, 3, 0x04, 3, 'V', 'VVV'), - 221: ('TODAY', 0, 0, 0x0a, 0, 'V', ''), - 222: ('VDB', 5, 7, 0x04, 7, 'V', 'VVVVVVV'), - 227: ('MEDIAN', 1, 30, 0x04, 1, 'V', 'R'), - 228: ('SUMPRODUCT', 1, 30, 0x04, 1, 'V', 'A'), - 229: ('SINH', 1, 1, 0x02, 1, 'V', 'V'), - 230: ('COSH', 1, 1, 0x02, 1, 'V', 'V'), - 231: ('TANH', 1, 1, 0x02, 1, 'V', 'V'), - 232: ('ASINH', 1, 1, 0x02, 1, 'V', 'V'), - 233: ('ACOSH', 1, 1, 0x02, 1, 'V', 'V'), - 234: ('ATANH', 1, 1, 0x02, 1, 'V', 'V'), - 235: ('DGET', 3, 3, 0x02, 3, 'V', 'RRR'), - 244: ('INFO', 1, 1, 0x02, 1, 'V', 'V'), - 247: ('DB', 4, 5, 0x04, 5, 'V', 'VVVVV'), - 252: ('FREQUENCY', 2, 2, 0x02, 2, 'A', 'RR'), - 261: ('ERROR.TYPE', 1, 1, 0x02, 1, 'V', 'V'), - 269: ('AVEDEV', 1, 30, 0x04, 1, 'V', 'R'), - 270: ('BETADIST', 3, 5, 0x04, 1, 'V', 'V'), - 271: ('GAMMALN', 1, 1, 0x02, 1, 'V', 'V'), - 272: ('BETAINV', 3, 5, 0x04, 1, 'V', 'V'), - 273: ('BINOMDIST', 4, 4, 0x02, 4, 'V', 'VVVV'), - 274: ('CHIDIST', 2, 2, 0x02, 2, 'V', 'VV'), - 275: ('CHIINV', 2, 2, 0x02, 2, 'V', 'VV'), - 276: ('COMBIN', 2, 2, 0x02, 2, 'V', 'VV'), - 277: ('CONFIDENCE', 3, 3, 0x02, 3, 'V', 'VVV'), - 278: ('CRITBINOM', 3, 3, 0x02, 3, 'V', 'VVV'), - 279: ('EVEN', 1, 1, 0x02, 1, 'V', 'V'), - 280: ('EXPONDIST', 3, 3, 0x02, 3, 'V', 'VVV'), - 281: ('FDIST', 3, 3, 0x02, 3, 'V', 'VVV'), - 282: ('FINV', 3, 3, 0x02, 3, 'V', 'VVV'), - 283: ('FISHER', 1, 1, 0x02, 1, 'V', 'V'), - 284: ('FISHERINV', 1, 1, 0x02, 1, 'V', 'V'), - 285: ('FLOOR', 2, 2, 0x02, 2, 'V', 'VV'), - 286: ('GAMMADIST', 4, 4, 0x02, 4, 'V', 'VVVV'), - 287: ('GAMMAINV', 3, 3, 0x02, 3, 'V', 'VVV'), - 288: ('CEILING', 2, 2, 0x02, 2, 'V', 'VV'), - 289: ('HYPGEOMDIST', 4, 4, 0x02, 4, 'V', 'VVVV'), - 290: ('LOGNORMDIST', 3, 3, 0x02, 3, 'V', 'VVV'), - 291: ('LOGINV', 3, 3, 0x02, 3, 'V', 'VVV'), - 292: ('NEGBINOMDIST', 3, 3, 0x02, 3, 'V', 'VVV'), - 293: ('NORMDIST', 4, 4, 0x02, 4, 'V', 'VVVV'), - 294: ('NORMSDIST', 1, 1, 0x02, 1, 'V', 'V'), - 295: ('NORMINV', 3, 3, 0x02, 3, 'V', 'VVV'), - 296: ('NORMSINV', 1, 1, 0x02, 1, 'V', 'V'), - 297: ('STANDARDIZE', 3, 3, 0x02, 3, 'V', 'VVV'), - 298: ('ODD', 1, 1, 0x02, 1, 'V', 'V'), - 299: ('PERMUT', 2, 2, 0x02, 2, 'V', 'VV'), - 300: ('POISSON', 3, 3, 0x02, 3, 'V', 'VVV'), - 301: ('TDIST', 3, 3, 0x02, 3, 'V', 'VVV'), - 302: ('WEIBULL', 4, 4, 0x02, 4, 'V', 'VVVV'), - 303: ('SUMXMY2', 2, 2, 0x02, 2, 'V', 'AA'), - 304: ('SUMX2MY2', 2, 2, 0x02, 2, 'V', 'AA'), - 305: ('SUMX2PY2', 2, 2, 0x02, 2, 'V', 'AA'), - 306: ('CHITEST', 2, 2, 0x02, 2, 'V', 'AA'), - 307: ('CORREL', 2, 2, 0x02, 2, 'V', 'AA'), - 308: ('COVAR', 2, 2, 0x02, 2, 'V', 'AA'), - 309: ('FORECAST', 3, 3, 0x02, 3, 'V', 'VAA'), - 310: ('FTEST', 2, 2, 0x02, 2, 'V', 'AA'), - 311: ('INTERCEPT', 2, 2, 0x02, 2, 'V', 'AA'), - 312: ('PEARSON', 2, 2, 0x02, 2, 'V', 'AA'), - 313: ('RSQ', 2, 2, 0x02, 2, 'V', 'AA'), - 314: ('STEYX', 2, 2, 0x02, 2, 'V', 'AA'), - 315: ('SLOPE', 2, 2, 0x02, 2, 'V', 'AA'), - 316: ('TTEST', 4, 4, 0x02, 4, 'V', 'AAVV'), - 317: ('PROB', 3, 4, 0x04, 3, 'V', 'AAV'), - 318: ('DEVSQ', 1, 30, 0x04, 1, 'V', 'R'), - 319: ('GEOMEAN', 1, 30, 0x04, 1, 'V', 'R'), - 320: ('HARMEAN', 1, 30, 0x04, 1, 'V', 'R'), - 321: ('SUMSQ', 0, 30, 0x04, 1, 'V', 'R'), - 322: ('KURT', 1, 30, 0x04, 1, 'V', 'R'), - 323: ('SKEW', 1, 30, 0x04, 1, 'V', 'R'), - 324: ('ZTEST', 2, 3, 0x04, 2, 'V', 'RV'), - 325: ('LARGE', 2, 2, 0x02, 2, 'V', 'RV'), - 326: ('SMALL', 2, 2, 0x02, 2, 'V', 'RV'), - 327: ('QUARTILE', 2, 2, 0x02, 2, 'V', 'RV'), - 328: ('PERCENTILE', 2, 2, 0x02, 2, 'V', 'RV'), - 329: ('PERCENTRANK', 2, 3, 0x04, 2, 'V', 'RV'), - 330: ('MODE', 1, 30, 0x04, 1, 'V', 'A'), - 331: ('TRIMMEAN', 2, 2, 0x02, 2, 'V', 'RV'), - 332: ('TINV', 2, 2, 0x02, 2, 'V', 'VV'), - 336: ('CONCATENATE', 0, 30, 0x04, 1, 'V', 'V'), - 337: ('POWER', 2, 2, 0x02, 2, 'V', 'VV'), - 342: ('RADIANS', 1, 1, 0x02, 1, 'V', 'V'), - 343: ('DEGREES', 1, 1, 0x02, 1, 'V', 'V'), - 344: ('SUBTOTAL', 2, 30, 0x04, 2, 'V', 'VR'), - 345: ('SUMIF', 2, 3, 0x04, 3, 'V', 'RVR'), - 346: ('COUNTIF', 2, 2, 0x02, 2, 'V', 'RV'), - 347: ('COUNTBLANK', 1, 1, 0x02, 1, 'V', 'R'), - 350: ('ISPMT', 4, 4, 0x02, 4, 'V', 'VVVV'), - 351: ('DATEDIF', 3, 3, 0x02, 3, 'V', 'VVV'), - 352: ('DATESTRING', 1, 1, 0x02, 1, 'V', 'V'), - 353: ('NUMBERSTRING', 2, 2, 0x02, 2, 'V', 'VV'), - 354: ('ROMAN', 1, 2, 0x04, 2, 'V', 'VV'), - 358: ('GETPIVOTDATA', 2, 2, 0x02, 2, 'V', 'RV'), - 359: ('HYPERLINK', 1, 2, 0x04, 2, 'V', 'VV'), - 360: ('PHONETIC', 1, 1, 0x02, 1, 'V', 'V'), - 361: ('AVERAGEA', 1, 30, 0x04, 1, 'V', 'R'), - 362: ('MAXA', 1, 30, 0x04, 1, 'V', 'R'), - 363: ('MINA', 1, 30, 0x04, 1, 'V', 'R'), - 364: ('STDEVPA', 1, 30, 0x04, 1, 'V', 'R'), - 365: ('VARPA', 1, 30, 0x04, 1, 'V', 'R'), - 366: ('STDEVA', 1, 30, 0x04, 1, 'V', 'R'), - 367: ('VARA', 1, 30, 0x04, 1, 'V', 'R'), - 368: ('BAHTTEXT', 1, 1, 0x02, 1, 'V', 'V'), - 369: ('THAIDAYOFWEEK', 1, 1, 0x02, 1, 'V', 'V'), - 370: ('THAIDIGIT', 1, 1, 0x02, 1, 'V', 'V'), - 371: ('THAIMONTHOFYEAR', 1, 1, 0x02, 1, 'V', 'V'), - 372: ('THAINUMSOUND', 1, 1, 0x02, 1, 'V', 'V'), - 373: ('THAINUMSTRING', 1, 1, 0x02, 1, 'V', 'V'), - 374: ('THAISTRINGLENGTH', 1, 1, 0x02, 1, 'V', 'V'), - 375: ('ISTHAIDIGIT', 1, 1, 0x02, 1, 'V', 'V'), - 376: ('ROUNDBAHTDOWN', 1, 1, 0x02, 1, 'V', 'V'), - 377: ('ROUNDBAHTUP', 1, 1, 0x02, 1, 'V', 'V'), - 378: ('THAIYEAR', 1, 1, 0x02, 1, 'V', 'V'), - 379: ('RTD', 2, 5, 0x04, 1, 'V', 'V'), - } - -tAttrNames = { - 0x00: "Skip??", # seen in SAMPLES.XLS which shipped with Excel 5.0 - 0x01: "Volatile", - 0x02: "If", - 0x04: "Choose", - 0x08: "Skip", - 0x10: "Sum", - 0x20: "Assign", - 0x40: "Space", - 0x41: "SpaceVolatile", - } - -_error_opcodes = {} -for _x in [0x07, 0x08, 0x0A, 0x0B, 0x1C, 0x1D, 0x2F]: - _error_opcodes[_x] = 1 -is_error_opcode = _error_opcodes.has_key - -tRangeFuncs = (min, max, min, max, min, max) -tIsectFuncs = (max, min, max, min, max, min) - -def do_box_funcs(box_funcs, boxa, boxb): - return tuple([ - func(numa, numb) - for func, numa, numb in zip(box_funcs, boxa.coords, boxb.coords) - ]) - -def adjust_cell_addr_biff8(rowval, colval, reldelta, browx=None, bcolx=None): - row_rel = (colval >> 15) & 1 - col_rel = (colval >> 14) & 1 - rowx = rowval - colx = colval & 0xff - if reldelta: - if row_rel and rowx >= 32768: - rowx -= 65536 - if col_rel and colx >= 128: - colx -= 256 - else: - if row_rel: - rowx -= browx - if col_rel: - colx -= bcolx - return rowx, colx, row_rel, col_rel - -def adjust_cell_addr_biff_le7( - rowval, colval, reldelta, browx=None, bcolx=None): - row_rel = (rowval >> 15) & 1 - col_rel = (rowval >> 14) & 1 - rowx = rowval & 0x3fff - colx = colval - if reldelta: - if row_rel and rowx >= 8192: - rowx -= 16384 - if col_rel and colx >= 128: - colx -= 256 - else: - if row_rel: - rowx -= browx - if col_rel: - colx -= bcolx - return rowx, colx, row_rel, col_rel - -def get_cell_addr(data, pos, bv, reldelta, browx=None, bcolx=None): - if bv >= 80: - rowval, colval = unpack("<HH", data[pos:pos+4]) - # print " rv=%04xh cv=%04xh" % (rowval, colval) - return adjust_cell_addr_biff8(rowval, colval, reldelta, browx, bcolx) - else: - rowval, colval = unpack("<HB", data[pos:pos+3]) - # print " rv=%04xh cv=%04xh" % (rowval, colval) - return adjust_cell_addr_biff_le7( - rowval, colval, reldelta, browx, bcolx) - -def get_cell_range_addr(data, pos, bv, reldelta, browx=None, bcolx=None): - if bv >= 80: - row1val, row2val, col1val, col2val = unpack("<HHHH", data[pos:pos+8]) - # print " rv=%04xh cv=%04xh" % (row1val, col1val) - # print " rv=%04xh cv=%04xh" % (row2val, col2val) - res1 = adjust_cell_addr_biff8(row1val, col1val, reldelta, browx, bcolx) - res2 = adjust_cell_addr_biff8(row2val, col2val, reldelta, browx, bcolx) - return res1, res2 - else: - row1val, row2val, col1val, col2val = unpack("<HHBB", data[pos:pos+6]) - # print " rv=%04xh cv=%04xh" % (row1val, col1val) - # print " rv=%04xh cv=%04xh" % (row2val, col2val) - res1 = adjust_cell_addr_biff_le7( - row1val, col1val, reldelta, browx, bcolx) - res2 = adjust_cell_addr_biff_le7( - row2val, col2val, reldelta, browx, bcolx) - return res1, res2 - -def get_externsheet_local_range(bk, refx, blah=0): - try: - info = bk._externsheet_info[refx] - except IndexError: - print "!!! get_externsheet_local_range: refx=%d, not in range(%d)" \ - % (refx, len(bk._externsheet_info)) - return (-101, -101) - ref_recordx, ref_first_sheetx, ref_last_sheetx = info - if ref_recordx == bk._supbook_addins_inx: - if blah: - print "/// get_externsheet_local_range(refx=%d) -> addins %r" % (refx, info) - assert ref_first_sheetx == 0xFFFE == ref_last_sheetx - return (-5, -5) - if ref_recordx != bk._supbook_locals_inx: - if blah: - print "/// get_externsheet_local_range(refx=%d) -> external %r" % (refx, info) - return (-4, -4) # external reference - if ref_first_sheetx == 0xFFFE == ref_last_sheetx: - if blah: - print "/// get_externsheet_local_range(refx=%d) -> unspecified sheet %r" % (refx, info) - return (-1, -1) # internal reference, any sheet - if ref_first_sheetx == 0xFFFF == ref_last_sheetx: - if blah: - print "/// get_externsheet_local_range(refx=%d) -> deleted sheet(s)" % (refx, ) - return (-2, -2) # internal reference, deleted sheet(s) - nsheets = len(bk._all_sheets_map) - if not(0 <= ref_first_sheetx <= ref_last_sheetx < nsheets): - if blah: - print "/// get_externsheet_local_range(refx=%d) -> %r" % (refx, info) - print "--- first/last sheet not in range(%d)" % nsheets - return (-102, -102) # stuffed up somewhere :-( - xlrd_sheetx1 = bk._all_sheets_map[ref_first_sheetx] - xlrd_sheetx2 = bk._all_sheets_map[ref_last_sheetx] - if not(0 <= xlrd_sheetx1 <= xlrd_sheetx2): - return (-3, -3) # internal reference, but to a macro sheet - return xlrd_sheetx1, xlrd_sheetx2 - -def get_externsheet_local_range_b57( - bk, raw_extshtx, ref_first_sheetx, ref_last_sheetx, blah=0): - if raw_extshtx > 0: - if blah: - print "/// get_externsheet_local_range_b57(raw_extshtx=%d) -> external" % raw_extshtx - return (-4, -4) # external reference - if ref_first_sheetx == -1 and ref_last_sheetx == -1: - return (-2, -2) # internal reference, deleted sheet(s) - nsheets = len(bk._all_sheets_map) - if not(0 <= ref_first_sheetx <= ref_last_sheetx < nsheets): - if blah: - print "/// get_externsheet_local_range_b57(%d, %d, %d) -> ???" \ - % (raw_extshtx, ref_first_sheetx, ref_last_sheetx) - print "--- first/last sheet not in range(%d)" % nsheets - return (-103, -103) # stuffed up somewhere :-( - xlrd_sheetx1 = bk._all_sheets_map[ref_first_sheetx] - xlrd_sheetx2 = bk._all_sheets_map[ref_last_sheetx] - if not(0 <= xlrd_sheetx1 <= xlrd_sheetx2): - return (-3, -3) # internal reference, but to a macro sheet - return xlrd_sheetx1, xlrd_sheetx2 - -class FormulaError(Exception): - pass - -oBOOL = 3 -oERR = 4 -oMSNG = 5 # tMissArg -oNUM = 2 -oREF = -1 -oREL = -2 -oSTRG = 1 -oUNK = 0 - -okind_dict = { - -2: "oREL", - -1: "oREF", - 0 : "oUNK", - 1 : "oSTRG", - 2 : "oNUM", - 3 : "oBOOL", - 4 : "oERR", - 5 : "oMSNG", - } - -listsep = ',' #### probably should depend on locale - -## -# Used in evaluating formulas. -# The following table describes the kinds and how their values -# are represented.</p> -# -# <table border="1" cellpadding="7"> -# <tr> -# <th>Kind symbol</th> -# <th>Kind number</th> -# <th>Value representation</th> -# </tr> -# <tr> -# <td>oBOOL</td> -# <td align="center">3</td> -# <td>integer: 0 => False; 1 => True</td> -# </tr> -# <tr> -# <td>oERR</td> -# <td align="center">4</td> -# <td>None, or an int error code (same as XL_CELL_ERROR in the Cell class). -# </td> -# </tr> -# <tr> -# <td>oMSNG</td> -# <td align="center">5</td> -# <td>Used by Excel as a placeholder for a missing (not supplied) function -# argument. Should *not* appear as a final formula result. Value is None.</td> -# </tr> -# <tr> -# <td>oNUM</td> -# <td align="center">2</td> -# <td>A float. Note that there is no way of distinguishing dates.</td> -# </tr> -# <tr> -# <td>oREF</td> -# <td align="center">-1</td> -# <td>The value is either None or a non-empty list of -# absolute Ref3D instances.<br> -# </td> -# </tr> -# <tr> -# <td>oREL</td> -# <td align="center">-2</td> -# <td>The value is None or a non-empty list of -# fully or partially relative Ref3D instances. -# </td> -# </tr> -# <tr> -# <td>oSTRG</td> -# <td align="center">1</td> -# <td>A Unicode string.</td> -# </tr> -# <tr> -# <td>oUNK</td> -# <td align="center">0</td> -# <td>The kind is unknown or ambiguous. The value is None</td> -# </tr> -# </table> -#<p></p> - -class Operand(object): - - ## - # None means that the actual value of the operand is a variable - # (depends on cell data), not a constant. - value = None - ## - # oUNK means that the kind of operand is not known unambiguously. - kind = oUNK - ## - # The reconstituted text of the original formula. Function names will be - # in English irrespective of the original language, which doesn't seem - # to be recorded anywhere. The separator is ",", not ";" or whatever else - # might be more appropriate for the end-user's locale; patches welcome. - text = '?' - - def __init__(self, akind=None, avalue=None, arank=0, atext='?'): - if akind is not None: - self.kind = akind - if avalue is not None: - self.value = avalue - self.rank = arank - # rank is an internal gizmo (operator precedence); - # it's used in reconstructing formula text. - self.text = atext - - def __repr__(self): - kind_text = okind_dict.get(self.kind, "?Unknown kind?") - return "Operand(kind=%s, value=%r, text=%r)" \ - % (kind_text, self.value, self.text) - -if CAN_SUBCLASS_BUILTIN: - _ref3d_base = tuple -else: - _ref3d_base = object - -## -# <p>Represents an absolute or relative 3-dimensional reference to a box -# of one or more cells.<br /> -# -- New in version 0.6.0 -# </p> -# -# <p>The <i>coords</i> attribute is a tuple of the form:<br /> -# (shtxlo, shtxhi, rowxlo, rowxhi, colxlo, colxhi)<br /> -# where 0 <= thingxlo <= thingx < thingxhi.<br /> -# Note that it is quite possible to have thingx > nthings; for example -# Print_Titles could have colxhi == 256 and/or rowxhi == 65536 -# irrespective of how many columns/rows are actually used in the worksheet. -# The caller will need to decide how to handle this situation. -# Keyword: IndexError :-) -# </p> -# -# <p>The components of the coords attribute are also available as individual -# attributes: shtxlo, shtxhi, rowxlo, rowxhi, colxlo, and colxhi.</p> -# -# <p>The <i>relflags</i> attribute is a 6-tuple of flags which indicate whether -# the corresponding (sheet|row|col)(lo|hi) is relative (1) or absolute (0).<br> -# Note that there is necessarily no information available as to what cell(s) -# the reference could possibly be relative to. The caller must decide what if -# any use to make of oREL operands. Note also that a partially relative -# reference may well be a typo. -# For example, define name A1Z10 as $a$1:$z10 (missing $ after z) -# while the cursor is on cell Sheet3!A27.<br> -# The resulting Ref3D instance will have coords = (2, 3, 0, -16, 0, 26) -# and relflags = (0, 0, 0, 1, 0, 0).<br> -# So far, only one possibility of a sheet-relative component in -# a reference has been noticed: a 2D reference located in the "current sheet". -# <br /> This will appear as coords = (0, 1, ...) and relflags = (1, 1, ...). - -class Ref3D(_ref3d_base): - - def __init__(self, atuple): - self.coords = atuple[0:6] - self.relflags = atuple[6:12] - if not self.relflags: - self.relflags = (0, 0, 0, 0, 0, 0) - (self.shtxlo, self.shtxhi, - self.rowxlo, self.rowxhi, - self.colxlo, self.colxhi) = self.coords - - def __repr__(self): - if not self.relflags or self.relflags == (0, 0, 0, 0, 0, 0): - return "Ref3D(coords=%r)" % (self.coords, ) - else: - return "Ref3D(coords=%r, relflags=%r)" \ - % (self.coords, self.relflags) - -tAdd = 0x03 -tSub = 0x04 -tMul = 0x05 -tDiv = 0x06 -tPower = 0x07 -tConcat = 0x08 -tLT, tLE, tEQ, tGE, tGT, tNE = range(0x09, 0x0F) - -import operator as opr - -def nop(x): - return x - -def _opr_pow(x, y): return x ** y - -def _opr_lt(x, y): return x < y -def _opr_le(x, y): return x <= y -def _opr_eq(x, y): return x == y -def _opr_ge(x, y): return x >= y -def _opr_gt(x, y): return x > y -def _opr_ne(x, y): return x != y - -def num2strg(num): - """Attempt to emulate Excel's default conversion - from number to string. - """ - s = str(num) - if s.endswith(".0"): - s = s[:-2] - return s - -_arith_argdict = {oNUM: nop, oSTRG: float} -_cmp_argdict = {oNUM: nop, oSTRG: nop} -# Seems no conversions done on relops; in Excel, "1" > 9 produces TRUE. -_strg_argdict = {oNUM:num2strg, oSTRG:nop} -binop_rules = { - tAdd: (_arith_argdict, oNUM, opr.add, 30, '+'), - tSub: (_arith_argdict, oNUM, opr.sub, 30, '-'), - tMul: (_arith_argdict, oNUM, opr.mul, 40, '*'), - tDiv: (_arith_argdict, oNUM, opr.div, 40, '/'), - tPower: (_arith_argdict, oNUM, _opr_pow, 50, '^',), - tConcat:(_strg_argdict, oSTRG, opr.add, 20, '&'), - tLT: (_cmp_argdict, oBOOL, _opr_lt, 10, '<'), - tLE: (_cmp_argdict, oBOOL, _opr_le, 10, '<='), - tEQ: (_cmp_argdict, oBOOL, _opr_eq, 10, '='), - tGE: (_cmp_argdict, oBOOL, _opr_ge, 10, '>='), - tGT: (_cmp_argdict, oBOOL, _opr_gt, 10, '>'), - tNE: (_cmp_argdict, oBOOL, _opr_ne, 10, '<>'), - } - -unop_rules = { - 0x13: (lambda x: -x, 70, '-', ''), # unary minus - 0x12: (lambda x: x, 70, '+', ''), # unary plus - 0x14: (lambda x: x / 100.0, 60, '', '%'),# percent - } - -LEAF_RANK = 90 -FUNC_RANK = 90 - -STACK_ALARM_LEVEL = 5 -STACK_PANIC_LEVEL = 10 - -def evaluate_name_formula(bk, nobj, namex, blah=0, level=0): - if level > STACK_ALARM_LEVEL: - blah = 1 - data = nobj.raw_formula - fmlalen = nobj.basic_formula_len - bv = bk.biff_version - reldelta = 1 # All defined name formulas use "Method B" [OOo docs] - if blah: - print "::: evaluate_name_formula %r %r %d %d %r level=%d" \ - % (namex, nobj.name, fmlalen, bv, data, level) - hex_char_dump(data, 0, fmlalen) - if level > STACK_PANIC_LEVEL: - raise XLRDError("Excessive indirect references in NAME formula") - sztab = szdict[bv] - pos = 0 - stack = [] - any_rel = 0 - any_err = 0 - any_external = 0 - unk_opnd = Operand(oUNK, None) - error_opnd = Operand(oERR, None) - spush = stack.append - - def do_binop(opcd, stk): - assert len(stk) >= 2 - bop = stk.pop() - aop = stk.pop() - argdict, result_kind, func, rank, sym = binop_rules[opcd] - otext = ''.join([ - '('[:aop.rank < rank], - aop.text, - ')'[:aop.rank < rank], - sym, - '('[:bop.rank < rank], - bop.text, - ')'[:bop.rank < rank], - ]) - resop = Operand(result_kind, None, rank, otext) - try: - bconv = argdict[bop.kind] - aconv = argdict[aop.kind] - except KeyError: - stk.append(resop) - return - if bop.value is None or aop.value is None: - stk.append(resop) - return - bval = bconv(bop.value) - aval = aconv(aop.value) - result = func(aval, bval) - if result_kind == oBOOL: - result = intbool(result) # -> 1 or 0 - resop.value = result - stk.append(resop) - - def do_unaryop(opcode, arglist, result_kind, stk): - assert len(stk) >= 1 - aop = stk.pop() - assert aop.kind in arglist - val = aop.value - func, rank, sym1, sym2 = unop_rules[opcode] - otext = ''.join([ - sym1, - '('[:aop.rank < rank], - aop.text, - ')'[:aop.rank < rank], - sym2, - ]) - if val is not None: - val = func(val) - stk.append(Operand(result_kind, val, rank, otext)) - - def not_in_name_formula(op_arg, oname_arg): - msg = "ERROR *** Token 0x%02x (%s) found in NAME formula" \ - % (op_arg, oname_arg) - raise FormulaError(msg) - - if fmlalen == 0: - stack = [unk_opnd] - - while 0 <= pos < fmlalen: - op = ord(data[pos]) - opcode = op & 0x1f - optype = (op & 0x60) >> 5 - if optype: - opx = opcode + 32 - else: - opx = opcode - oname = onames[opx] # + [" RVA"][optype] - sz = sztab[opx] - if blah: - print "Pos:%d Op:0x%02x Name:t%s Sz:%d opcode:%02xh optype:%02xh" \ - % (pos, op, oname, sz, opcode, optype) - print "Stack =", stack - if sz == -2: - msg = 'ERROR *** Unexpected token 0x%02x ("%s"); biff_version=%d' \ - % (op, oname, bv) - raise FormulaError(msg) - if not optype: - if 0x00 <= opcode <= 0x02: # unk_opnd, tExp, tTbl - not_in_name_formula(op, oname) - elif 0x03 <= opcode <= 0x0E: - # Add, Sub, Mul, Div, Power - # tConcat - # tLT, ..., tNE - do_binop(opcode, stack) - elif opcode == 0x0F: # tIsect - if blah: print >> bk.logfile, "tIsect pre", stack - assert len(stack) >= 2 - bop = stack.pop() - aop = stack.pop() - sym = ' ' - rank = 80 ########## check ####### - otext = ''.join([ - '('[:aop.rank < rank], - aop.text, - ')'[:aop.rank < rank], - sym, - '('[:bop.rank < rank], - bop.text, - ')'[:bop.rank < rank], - ]) - res = Operand(oREF) - res.text = otext - if bop.kind == oERR or aop.kind == oERR: - res.kind = oERR - elif bop.kind == oUNK or aop.kind == oUNK: - # This can happen with undefined - # (go search in the current sheet) labels. - # For example =Bob Sales - # Each label gets a NAME record with an empty formula (!) - # Evaluation of the tName token classifies it as oUNK - # res.kind = oREF - pass - elif bop.kind == oREF == aop.kind: - if aop.value is not None and bop.value is not None: - assert len(aop.value) == 1 - assert len(bop.value) == 1 - coords = do_box_funcs( - tIsectFuncs, aop.value[0], bop.value[0]) - res.value = [Ref3D(coords)] - elif bop.kind == oREL == aop.kind: - res.kind = oREL - if aop.value is not None and bop.value is not None: - assert len(aop.value) == 1 - assert len(bop.value) == 1 - coords = do_box_funcs( - tIsectFuncs, aop.value[0], bop.value[0]) - relfa = aop.value[0].relflags - relfb = bop.value[0].relflags - if relfa == relfb: - res.value = [Ref3D(coords + relfa)] - else: - pass - spush(res) - if blah: print >> bk.logfile, "tIsect post", stack - elif opcode == 0x10: # tList - if blah: print >> bk.logfile, "tList pre", stack - assert len(stack) >= 2 - bop = stack.pop() - aop = stack.pop() - sym = ',' - rank = 80 ########## check ####### - otext = ''.join([ - '('[:aop.rank < rank], - aop.text, - ')'[:aop.rank < rank], - sym, - '('[:bop.rank < rank], - bop.text, - ')'[:bop.rank < rank], - ]) - res = Operand(oREF, None, rank, otext) - if bop.kind == oERR or aop.kind == oERR: - res.kind = oERR - elif bop.kind in (oREF, oREL) and aop.kind in (oREF, oREL): - res.kind = oREF - if aop.kind == oREL or bop.kind == oREL: - res.kind = oREL - if aop.value is not None and bop.value is not None: - assert len(aop.value) >= 1 - assert len(bop.value) == 1 - res.value = aop.value + bop.value - else: - pass - spush(res) - if blah: print >> bk.logfile, "tList post", stack - elif opcode == 0x11: # tRange - if blah: print >> bk.logfile, "tRange pre", stack - assert len(stack) >= 2 - bop = stack.pop() - aop = stack.pop() - sym = ':' - rank = 80 ########## check ####### - otext = ''.join([ - '('[:aop.rank < rank], - aop.text, - ')'[:aop.rank < rank], - sym, - '('[:bop.rank < rank], - bop.text, - ')'[:bop.rank < rank], - ]) - res = Operand(oREF, None, rank, otext) - if bop.kind == oERR or aop.kind == oERR: - res = oERR - elif bop.kind == oREF == aop.kind: - if aop.value is not None and bop.value is not None: - assert len(aop.value) == 1 - assert len(bop.value) == 1 - coords = do_box_funcs( - tRangeFuncs, aop.value[0], bop.value[0]) - res.value = [Ref3D(coords)] - elif bop.kind == oREL == aop.kind: - res.kind = oREL - if aop.value is not None and bop.value is not None: - assert len(aop.value) == 1 - assert len(bop.value) == 1 - coords = do_box_funcs( - tRangeFuncs, aop.value[0], bop.value[0]) - relfa = aop.value[0].relflags - relfb = bop.value[0].relflags - if relfa == relfb: - res.value = [Ref3D(coords + relfa)] - else: - pass - spush(res) - if blah: print >> bk.logfile, "tRange post", stack - elif 0x12 <= opcode <= 0x14: # tUplus, tUminus, tPercent - do_unaryop(opcode, (oUNK, oNUM,), oNUM, stack) - elif opcode == 0x15: # tParen - # source cosmetics - pass - elif opcode == 0x16: # tMissArg - spush(Operand(oMSNG, None, LEAF_RANK, '')) - elif opcode == 0x17: # tStr - if bv <= 70: - strg, newpos = unpack_string_update_pos( - data, pos+1, bk.encoding, lenlen=1) - else: - strg, newpos = unpack_unicode_update_pos( - data, pos+1, lenlen=1) - sz = newpos - pos - if blah: print >> bk.logfile, " sz=%d strg=%r" % (sz, strg) - text = '"' + strg.replace('"', '""') + '"' - spush(Operand(oSTRG, strg, LEAF_RANK, text)) - elif opcode == 0x18: # tExtended - # new with BIFF 8 - assert bv >= 80 - # not in OOo docs - raise FormulaError("tExtended token not implemented") - elif opcode == 0x19: # tAttr - subop, nc = unpack("<BH", data[pos+1:pos+4]) - subname = tAttrNames.get(subop, "??Unknown??") - if subop == 0x04: # Choose - sz = nc * 2 + 6 - elif subop == 0x10: # Sum (single arg) - sz = 4 - if blah: print >> bk.logfile, "tAttrSum", stack - assert len(stack) >= 1 - aop = stack[-1] - otext = 'SUM(%s)' % aop.text - stack[-1] = Operand(oNUM, None, FUNC_RANK, otext) - else: - sz = 4 - if blah: - print " subop=%02xh subname=t%s sz=%d nc=%02xh" \ - % (subop, subname, sz, nc) - elif 0x1A <= opcode <= 0x1B: # tSheet, tEndSheet - assert bv < 50 - raise FormulaError("tSheet & tEndsheet tokens not implemented") - elif 0x1C <= opcode <= 0x1F: # tErr, tBool, tInt, tNum - inx = opcode - 0x1C - nb = [1, 1, 2, 8][inx] - kind = [oERR, oBOOL, oNUM, oNUM][inx] - value, = unpack("<" + "BBHd"[inx], data[pos+1:pos+1+nb]) - if inx == 2: # tInt - value = float(value) - text = str(value) - elif inx == 3: # tNum - text = str(value) - elif inx == 1: # tBool - text = ('FALSE', 'TRUE')[value] - else: - text = '"' +error_text_from_code[value] + '"' - spush(Operand(kind, value, LEAF_RANK, text)) - else: - raise FormulaError("Unhandled opcode: 0x%02x" % opcode) - if sz <= 0: - raise FormulaError("Size not set for opcode 0x%02x" % opcode) - pos += sz - continue - if opcode == 0x00: # tArray - spush(unk_opnd) - elif opcode == 0x01: # tFunc - nb = 1 + int(bv >= 40) - funcx = unpack("<" + " BH"[nb], data[pos+1:pos+1+nb])[0] - func_attrs = func_defs.get(funcx, None) - if not func_attrs: - print >> bk.logfile, "*** formula/tFunc unknown FuncID:%d" \ - % funcx - spush(unk_opnd) - else: - func_name, nargs = func_attrs[:2] - if blah: - print " FuncID=%d name=%s nargs=%d" \ - % (funcx, func_name, nargs) - assert len(stack) >= nargs - argtext = listsep.join([arg.text for arg in stack[-nargs:]]) - otext = "%s(%s)" % (func_name, argtext) - del stack[-nargs:] - res = Operand(oUNK, None, FUNC_RANK, otext) - spush(res) - elif opcode == 0x02: #tFuncVar - nb = 1 + int(bv >= 40) - nargs, funcx = unpack("<B" + " BH"[nb], data[pos+1:pos+2+nb]) - prompt, nargs = divmod(nargs, 128) - macro, funcx = divmod(funcx, 32768) - if blah: - print " FuncID=%d nargs=%d macro=%d prompt=%d" \ - % (funcx, nargs, macro, prompt) - func_attrs = func_defs.get(funcx, None) - if not func_attrs: - print >> bk.logfile, "*** formula/tFuncVar unknown FuncID:%d" \ - % funcx - spush(unk_opnd) - else: - func_name, minargs, maxargs = func_attrs[:3] - if blah: - print " name: %r, min~max args: %d~%d" \ - % (func_name, minargs, maxargs) - assert minargs <= nargs <= maxargs - assert len(stack) >= nargs - assert len(stack) >= nargs - argtext = listsep.join([arg.text for arg in stack[-nargs:]]) - otext = "%s(%s)" % (func_name, argtext) - res = Operand(oUNK, None, FUNC_RANK, otext) - if funcx == 1: # IF - testarg = stack[-nargs] - if testarg.kind not in (oNUM, oBOOL): - if blah and testarg.kind != oUNK: - print "IF testarg kind?" - elif testarg.value not in (0, 1): - if blah and testarg.value is not None: - print "IF testarg value?" - else: - if nargs == 2 and not testarg.value: - # IF(FALSE, tv) => FALSE - res.kind, res.value = oBOOL, 0 - else: - respos = -nargs + 2 - int(testarg.value) - chosen = stack[respos] - if chosen.kind == oMSNG: - res.kind, res.value = oNUM, 0 - else: - res.kind, res.value = chosen.kind, chosen.value - if blah: - print "$$$$$$ IF => constant" - elif funcx == 100: # CHOOSE - testarg = stack[-nargs] - if testarg.kind == oNUM: - if 1 <= testarg.value < nargs: - chosen = stack[-nargs + int(testarg.value)] - if chosen.kind == oMSNG: - res.kind, res.value = oNUM, 0 - else: - res.kind, res.value = chosen.kind, chosen.value - del stack[-nargs:] - spush(res) - elif opcode == 0x03: #tName - tgtnamex = unpack("<H", data[pos+1:pos+3])[0] - 1 - # Only change with BIFF version is number of trailing UNUSED bytes! - if blah: print >> bk.logfile, " tgtnamex=%d" % tgtnamex - tgtobj = bk.name_obj_list[tgtnamex] - if not tgtobj.evaluated: - ### recursive ### - evaluate_name_formula(bk, tgtobj, tgtnamex, blah, level+1) - if tgtobj.macro or tgtobj.binary \ - or tgtobj.any_err: - if blah: - tgtobj.dump( - bk.logfile, - header="!!! tgtobj has problems!!!", - footer="----------- --------", - ) - res = Operand(oUNK, None) - any_err = any_err or tgtobj.macro or tgtobj.binary or tgtobj.any_err - any_rel = any_rel or tgtobj.any_rel - else: - assert len(tgtobj.stack) == 1 - res = copy.deepcopy(tgtobj.stack[0]) - res.rank = LEAF_RANK - if tgtobj.scope == -1: - res.text = tgtobj.name - else: - res.text = "%s!%s" \ - % (bk._sheet_names[tgtobj.scope], tgtobj.name) - if blah: - print >> bk.logfile, " tName: setting text to", repr(res.text) - spush(res) - elif opcode == 0x04: # tRef - # not_in_name_formula(op, oname) - res = get_cell_addr(data, pos+1, bv, reldelta) - if blah: print >> bk.logfile, " ", res - rowx, colx, row_rel, col_rel = res - shx1 = shx2 = 0 ####### N.B. relative to the CURRENT SHEET - any_rel = 1 - coords = (shx1, shx2+1, rowx, rowx+1, colx, colx+1) - if blah: print >> bk.logfile, " ", coords - res = Operand(oUNK, None) - if optype == 1: - relflags = (1, 1, row_rel, row_rel, col_rel, col_rel) - res = Operand(oREL, [Ref3D(coords + relflags)]) - spush(res) - elif opcode == 0x05: # tArea - # not_in_name_formula(op, oname) - res1, res2 = get_cell_range_addr(data, pos+1, bv, reldelta) - if blah: print >> bk.logfile, " ", res1, res2 - rowx1, colx1, row_rel1, col_rel1 = res1 - rowx2, colx2, row_rel2, col_rel2 = res2 - shx1 = shx2 = 0 ####### N.B. relative to the CURRENT SHEET - any_rel = 1 - coords = (shx1, shx2+1, rowx1, rowx2+1, colx1, colx2+1) - if blah: print >> bk.logfile, " ", coords - res = Operand(oUNK, None) - if optype == 1: - relflags = (1, 1, row_rel1, row_rel2, col_rel1, col_rel2) - res = Operand(oREL, [Ref3D(coords + relflags)]) - spush(res) - elif opcode == 0x06: # tMemArea - not_in_name_formula(op, oname) - elif opcode == 0x09: # tMemFunc - nb = unpack("<H", data[pos+1:pos+3])[0] - if blah: print >> bk.logfile, " %d bytes of cell ref formula" % nb - # no effect on stack - elif opcode == 0x0C: #tRefN - not_in_name_formula(op, oname) - # res = get_cell_addr(data, pos+1, bv, reldelta=1) - # # note *ALL* tRefN usage has signed offset for relative addresses - # any_rel = 1 - # if blah: print >> bk.logfile, " ", res - # spush(res) - elif opcode == 0x0D: #tAreaN - not_in_name_formula(op, oname) - # res = get_cell_range_addr(data, pos+1, bv, reldelta=1) - # # note *ALL* tAreaN usage has signed offset for relative addresses - # any_rel = 1 - # if blah: print >> bk.logfile, " ", res - elif opcode == 0x1A: # tRef3d - if bv >= 80: - res = get_cell_addr(data, pos+3, bv, reldelta) - refx = unpack("<H", data[pos+1:pos+3])[0] - shx1, shx2 = get_externsheet_local_range(bk, refx, blah) - else: - res = get_cell_addr(data, pos+15, bv, reldelta) - raw_extshtx, raw_shx1, raw_shx2 = \ - unpack("<hxxxxxxxxhh", data[pos+1:pos+15]) - if blah: - print >> bk.logfile, "tRef3d", raw_extshtx, raw_shx1, raw_shx2 - shx1, shx2 = get_externsheet_local_range_b57( - bk, raw_extshtx, raw_shx1, raw_shx2, blah) - rowx, colx, row_rel, col_rel = res - is_rel = row_rel or col_rel - any_rel = any_rel or is_rel - coords = (shx1, shx2+1, rowx, rowx+1, colx, colx+1) - any_err |= shx1 < -1 - if blah: print >> bk.logfile, " ", coords - res = Operand(oUNK, None) - if is_rel: - relflags = (0, 0, row_rel, row_rel, col_rel, col_rel) - ref3d = Ref3D(coords + relflags) - res.kind = oREL - res.text = rangename3drel(bk, ref3d) - else: - ref3d = Ref3D(coords) - res.kind = oREF - res.text = rangename3d(bk, ref3d) - res.rank = LEAF_RANK - if optype == 1: - res.value = [ref3d] - spush(res) - elif opcode == 0x1B: # tArea3d - if bv >= 80: - res1, res2 = get_cell_range_addr(data, pos+3, bv, reldelta) - refx = unpack("<H", data[pos+1:pos+3])[0] - shx1, shx2 = get_externsheet_local_range(bk, refx, blah) - else: - res1, res2 = get_cell_range_addr(data, pos+15, bv, reldelta) - raw_extshtx, raw_shx1, raw_shx2 = \ - unpack("<hxxxxxxxxhh", data[pos+1:pos+15]) - if blah: - print >> bk.logfile, "tArea3d", raw_extshtx, raw_shx1, raw_shx2 - shx1, shx2 = get_externsheet_local_range_b57( - bk, raw_extshtx, raw_shx1, raw_shx2, blah) - any_err |= shx1 < -1 - rowx1, colx1, row_rel1, col_rel1 = res1 - rowx2, colx2, row_rel2, col_rel2 = res2 - is_rel = row_rel1 or col_rel1 or row_rel2 or col_rel2 - any_rel = any_rel or is_rel - coords = (shx1, shx2+1, rowx1, rowx2+1, colx1, colx2+1) - if blah: print >> bk.logfile, " ", coords - res = Operand(oUNK, None) - if is_rel: - relflags = (0, 0, row_rel1, row_rel2, col_rel1, col_rel2) - ref3d = Ref3D(coords + relflags) - res.kind = oREL - res.text = rangename3drel(bk, ref3d) - else: - ref3d = Ref3D(coords) - res.kind = oREF - res.text = rangename3d(bk, ref3d) - res.rank = LEAF_RANK - if optype == 1: - res.value = [ref3d] - - spush(res) - elif opcode == 0x19: # tNameX - dodgy = 0 - res = Operand(oUNK, None) - if bv >= 80: - refx, tgtnamex = unpack("<HH", data[pos+1:pos+5]) - tgtnamex -= 1 - origrefx = refx - else: - refx, tgtnamex = unpack("<hxxxxxxxxH", data[pos+1:pos+13]) - tgtnamex -= 1 - origrefx = refx - if refx > 0: - refx -= 1 - elif refx < 0: - refx = -refx - 1 - else: - dodgy = 1 - if blah: - print >> bk.logfile, \ - " origrefx=%d refx=%d tgtnamex=%d dodgy=%d" \ - % (origrefx, refx, tgtnamex, dodgy) - if tgtnamex == namex: - if blah: print >> bk.logfile, "!!!! Self-referential !!!!" - dodgy = any_err = 1 - if not dodgy: - if bv >= 80: - shx1, shx2 = get_externsheet_local_range(bk, refx, blah) - elif origrefx > 0: - shx1, shx2 = (-4, -4) # external ref - else: - exty = bk._externsheet_type_b57[refx] - if exty == 4: # non-specific sheet in own doc't - shx1, shx2 = (-1, -1) # internal, any sheet - else: - shx1, shx2 = (-666, -666) - if dodgy or shx1 < -1: - otext = "<<Name #%d in external(?) file #%d>>" \ - % (tgtnamex, origrefx) - res = Operand(oUNK, None, LEAF_RANK, otext) - else: - tgtobj = bk.name_obj_list[tgtnamex] - if not tgtobj.evaluated: - ### recursive ### - evaluate_name_formula(bk, tgtobj, tgtnamex, blah, level+1) - if tgtobj.macro or tgtobj.binary \ - or tgtobj.any_err: - if blah: - tgtobj.dump( - bk.logfile, - header="!!! bad tgtobj !!!", - footer="------------------", - ) - res = Operand(oUNK, None) - any_err = any_err or tgtobj.macro or tgtobj.binary or tgtobj.any_err - any_rel = any_rel or tgtobj.any_rel - else: - assert len(tgtobj.stack) == 1 - res = copy.deepcopy(tgtobj.stack[0]) - res.rank = LEAF_RANK - if tgtobj.scope == -1: - res.text = tgtobj.name - else: - res.text = "%s!%s" \ - % (bk._sheet_names[tgtobj.scope], tgtobj.name) - if blah: - print >> bk.logfile, " tNameX: setting text to", repr(res.text) - spush(res) - elif is_error_opcode(opcode): - any_err = 1 - spush(error_opnd) - else: - if blah: - print >> bk.logfile, "FORMULA: /// Not handled yet: t" + oname - any_err = 1 - if sz <= 0: - raise FormulaError("Fatal: token size is not positive") - pos += sz - any_rel = not not any_rel - if blah: - print "End of formula. level=%d any_rel=%d any_err=%d stack=%r" % \ - (level, not not any_rel, any_err, stack) - if len(stack) >= 2: - print "*** Stack has unprocessed args" - print - nobj.stack = stack - if len(stack) != 1: - nobj.result = None - else: - nobj.result = stack[0] - nobj.any_rel = any_rel - nobj.any_err = any_err - nobj.any_external = any_external - nobj.evaluated = 1 - -#### under construction #### -def decompile_formula(bk, fmla, fmlalen, - reldelta, browx=None, bcolx=None, - # browx & bcolx are required when reldelta == 0 - blah=0, level=0): - if level > STACK_ALARM_LEVEL: - blah = 1 - data = fmla - bv = bk.biff_version - if blah: - print "::: decompile_formula len=%d reldelta=%d %r level=%d" \ - % (fmlalen, reldelta, data, level) - hex_char_dump(data, 0, fmlalen) - if level > STACK_PANIC_LEVEL: - raise XLRDError("Excessive indirect references in formula") - sztab = szdict[bv] - pos = 0 - stack = [] - any_rel = 0 - any_err = 0 - any_external = 0 - unk_opnd = Operand(oUNK, None) - error_opnd = Operand(oERR, None) - spush = stack.append - - def do_binop(opcd, stk): - assert len(stk) >= 2 - bop = stk.pop() - aop = stk.pop() - argdict, result_kind, func, rank, sym = binop_rules[opcd] - otext = ''.join([ - '('[:aop.rank < rank], - aop.text, - ')'[:aop.rank < rank], - sym, - '('[:bop.rank < rank], - bop.text, - ')'[:bop.rank < rank], - ]) - resop = Operand(result_kind, None, rank, otext) - stk.append(resop) - - def do_unaryop(opcode, arglist, result_kind, stk): - assert len(stk) >= 1 - aop = stk.pop() - assert aop.kind in arglist - func, rank, sym1, sym2 = unop_rules[opcode] - otext = ''.join([ - sym1, - '('[:aop.rank < rank], - aop.text, - ')'[:aop.rank < rank], - sym2, - ]) - stk.append(Operand(result_kind, None, rank, otext)) - - def not_in_name_formula(op_arg, oname_arg): - msg = "ERROR *** Unexpected token 0x%02x (%s) found in formula" \ - % (op_arg, oname_arg) - # print msg - raise FormulaError(msg) - - if fmlalen == 0: - stack = [unk_opnd] - - while 0 <= pos < fmlalen: - op = ord(data[pos]) - opcode = op & 0x1f - optype = (op & 0x60) >> 5 - if optype: - opx = opcode + 32 - else: - opx = opcode - oname = onames[opx] # + [" RVA"][optype] - sz = sztab[opx] - if blah: - print "Pos:%d Op:0x%02x opname:t%s Sz:%d opcode:%02xh optype:%02xh" \ - % (pos, op, oname, sz, opcode, optype) - print "Stack =", stack - if sz == -2: - msg = 'ERROR *** Unexpected token 0x%02x ("%s"); biff_version=%d' \ - % (op, oname, bv) - raise FormulaError(msg) - if not optype: - if 0x00 <= opcode <= 0x02: # unk_opnd, tExp, tTbl - not_in_name_formula(op, oname) - elif 0x03 <= opcode <= 0x0E: - # Add, Sub, Mul, Div, Power - # tConcat - # tLT, ..., tNE - do_binop(opcode, stack) - elif opcode == 0x0F: # tIsect - if blah: print >> bk.logfile, "tIsect pre", stack - assert len(stack) >= 2 - bop = stack.pop() - aop = stack.pop() - sym = ' ' - rank = 80 ########## check ####### - otext = ''.join([ - '('[:aop.rank < rank], - aop.text, - ')'[:aop.rank < rank], - sym, - '('[:bop.rank < rank], - bop.text, - ')'[:bop.rank < rank], - ]) - res = Operand(oREF) - res.text = otext - if bop.kind == oERR or aop.kind == oERR: - res.kind = oERR - elif bop.kind == oUNK or aop.kind == oUNK: - # This can happen with undefined - # (go search in the current sheet) labels. - # For example =Bob Sales - # Each label gets a NAME record with an empty formula (!) - # Evaluation of the tName token classifies it as oUNK - # res.kind = oREF - pass - elif bop.kind == oREF == aop.kind: - pass - elif bop.kind == oREL == aop.kind: - res.kind = oREL - else: - pass - spush(res) - if blah: print >> bk.logfile, "tIsect post", stack - elif opcode == 0x10: # tList - if blah: print >> bk.logfile, "tList pre", stack - assert len(stack) >= 2 - bop = stack.pop() - aop = stack.pop() - sym = ',' - rank = 80 ########## check ####### - otext = ''.join([ - '('[:aop.rank < rank], - aop.text, - ')'[:aop.rank < rank], - sym, - '('[:bop.rank < rank], - bop.text, - ')'[:bop.rank < rank], - ]) - res = Operand(oREF, None, rank, otext) - if bop.kind == oERR or aop.kind == oERR: - res.kind = oERR - elif bop.kind in (oREF, oREL) and aop.kind in (oREF, oREL): - res.kind = oREF - if aop.kind == oREL or bop.kind == oREL: - res.kind = oREL - else: - pass - spush(res) - if blah: print >> bk.logfile, "tList post", stack - elif opcode == 0x11: # tRange - if blah: print >> bk.logfile, "tRange pre", stack - assert len(stack) >= 2 - bop = stack.pop() - aop = stack.pop() - sym = ':' - rank = 80 ########## check ####### - otext = ''.join([ - '('[:aop.rank < rank], - aop.text, - ')'[:aop.rank < rank], - sym, - '('[:bop.rank < rank], - bop.text, - ')'[:bop.rank < rank], - ]) - res = Operand(oREF, None, rank, otext) - if bop.kind == oERR or aop.kind == oERR: - res = oERR - elif bop.kind == oREF == aop.kind: - pass - else: - pass - spush(res) - if blah: print >> bk.logfile, "tRange post", stack - elif 0x12 <= opcode <= 0x14: # tUplus, tUminus, tPercent - do_unaryop(opcode, (oUNK, oNUM,), oNUM, stack) - elif opcode == 0x15: # tParen - # source cosmetics - pass - elif opcode == 0x16: # tMissArg - spush(Operand(oMSNG, None, LEAF_RANK, '')) - elif opcode == 0x17: # tStr - if bv <= 70: - strg, newpos = unpack_string_update_pos( - data, pos+1, bk.encoding, lenlen=1) - else: - strg, newpos = unpack_unicode_update_pos( - data, pos+1, lenlen=1) - sz = newpos - pos - if blah: print >> bk.logfile, " sz=%d strg=%r" % (sz, strg) - text = '"' + strg.replace('"', '""') + '"' - spush(Operand(oSTRG, None, LEAF_RANK, text)) - elif opcode == 0x18: # tExtended - # new with BIFF 8 - assert bv >= 80 - # not in OOo docs - raise FormulaError("tExtended token not implemented") - elif opcode == 0x19: # tAttr - subop, nc = unpack("<BH", data[pos+1:pos+4]) - subname = tAttrNames.get(subop, "??Unknown??") - if subop == 0x04: # Choose - sz = nc * 2 + 6 - elif subop == 0x10: # Sum (single arg) - sz = 4 - if blah: print >> bk.logfile, "tAttrSum", stack - assert len(stack) >= 1 - aop = stack[-1] - otext = 'SUM(%s)' % aop.text - stack[-1] = Operand(oNUM, None, FUNC_RANK, otext) - else: - sz = 4 - if blah: - print " subop=%02xh subname=t%s sz=%d nc=%02xh" \ - % (subop, subname, sz, nc) - elif 0x1A <= opcode <= 0x1B: # tSheet, tEndSheet - assert bv < 50 - raise FormulaError("tSheet & tEndsheet tokens not implemented") - elif 0x1C <= opcode <= 0x1F: # tErr, tBool, tInt, tNum - inx = opcode - 0x1C - nb = [1, 1, 2, 8][inx] - kind = [oERR, oBOOL, oNUM, oNUM][inx] - value, = unpack("<" + "BBHd"[inx], data[pos+1:pos+1+nb]) - if inx == 2: # tInt - value = float(value) - text = str(value) - elif inx == 3: # tNum - text = str(value) - elif inx == 1: # tBool - text = ('FALSE', 'TRUE')[value] - else: - text = '"' +error_text_from_code[value] + '"' - spush(Operand(kind, None, LEAF_RANK, text)) - else: - raise FormulaError("Unhandled opcode: 0x%02x" % opcode) - if sz <= 0: - raise FormulaError("Size not set for opcode 0x%02x" % opcode) - pos += sz - continue - if opcode == 0x00: # tArray - spush(unk_opnd) - elif opcode == 0x01: # tFunc - nb = 1 + int(bv >= 40) - funcx = unpack("<" + " BH"[nb], data[pos+1:pos+1+nb])[0] - func_attrs = func_defs.get(funcx, None) - if not func_attrs: - print >> bk.logfile, "*** formula/tFunc unknown FuncID:%d" % funcx - spush(unk_opnd) - else: - func_name, nargs = func_attrs[:2] - if blah: - print " FuncID=%d name=%s nargs=%d" \ - % (funcx, func_name, nargs) - assert len(stack) >= nargs - argtext = listsep.join([arg.text for arg in stack[-nargs:]]) - otext = "%s(%s)" % (func_name, argtext) - del stack[-nargs:] - res = Operand(oUNK, None, FUNC_RANK, otext) - spush(res) - elif opcode == 0x02: #tFuncVar - nb = 1 + int(bv >= 40) - nargs, funcx = unpack("<B" + " BH"[nb], data[pos+1:pos+2+nb]) - prompt, nargs = divmod(nargs, 128) - macro, funcx = divmod(funcx, 32768) - if blah: - print " FuncID=%d nargs=%d macro=%d prompt=%d" \ - % (funcx, nargs, macro, prompt) - #### TODO #### if funcx == 255: # call add-in function - if funcx == 255: - func_attrs = ("CALL_ADDIN", 1, 30) - else: - func_attrs = func_defs.get(funcx, None) - if not func_attrs: - print >> bk.logfile, "*** formula/tFuncVar unknown FuncID:%d" \ - % funcx - spush(unk_opnd) - else: - func_name, minargs, maxargs = func_attrs[:3] - if blah: - print " name: %r, min~max args: %d~%d" \ - % (func_name, minargs, maxargs) - assert minargs <= nargs <= maxargs - assert len(stack) >= nargs - assert len(stack) >= nargs - argtext = listsep.join([arg.text for arg in stack[-nargs:]]) - otext = "%s(%s)" % (func_name, argtext) - res = Operand(oUNK, None, FUNC_RANK, otext) - del stack[-nargs:] - spush(res) - elif opcode == 0x03: #tName - tgtnamex = unpack("<H", data[pos+1:pos+3])[0] - 1 - # Only change with BIFF version is number of trailing UNUSED bytes! - if blah: print >> bk.logfile, " tgtnamex=%d" % tgtnamex - tgtobj = bk.name_obj_list[tgtnamex] - if tgtobj.scope == -1: - otext = tgtobj.name - else: - otext = "%s!%s" % (bk._sheet_names[tgtobj.scope], tgtobj.name) - if blah: - print >> bk.logfile, " tName: setting text to", repr(otext) - res = Operand(oUNK, None, LEAF_RANK, otext) - spush(res) - elif opcode == 0x04: # tRef - res = get_cell_addr(data, pos+1, bv, reldelta, browx, bcolx) - if blah: print >> bk.logfile, " ", res - rowx, colx, row_rel, col_rel = res - is_rel = row_rel or col_rel - if is_rel: - okind = oREL - else: - okind = oREF - otext = cellnamerel(rowx, colx, row_rel, col_rel) - res = Operand(okind, None, LEAF_RANK, otext) - spush(res) - elif opcode == 0x05: # tArea - res1, res2 = get_cell_range_addr( - data, pos+1, bv, reldelta, browx, bcolx) - if blah: print >> bk.logfile, " ", res1, res2 - rowx1, colx1, row_rel1, col_rel1 = res1 - rowx2, colx2, row_rel2, col_rel2 = res2 - coords = (rowx1, rowx2+1, colx1, colx2+1) - relflags = (row_rel1, row_rel2, col_rel1, col_rel2) - is_rel = intbool(sum(relflags)) - if is_rel: - okind = oREL - else: - okind = oREF - if blah: print >> bk.logfile, " ", coords, relflags - otext = rangename2drel(coords, relflags) - res = Operand(okind, None, LEAF_RANK, otext) - spush(res) - elif opcode == 0x06: # tMemArea - not_in_name_formula(op, oname) - elif opcode == 0x09: # tMemFunc - nb = unpack("<H", data[pos+1:pos+3])[0] - if blah: print >> bk.logfile, " %d bytes of cell ref formula" % nb - # no effect on stack - elif opcode == 0x0C: #tRefN - not_in_name_formula(op, oname) - # res = get_cell_addr(data, pos+1, bv, reldelta=1) - # # note *ALL* tRefN usage has signed offset for relative addresses - # any_rel = 1 - # if blah: print >> bk.logfile, " ", res - # spush(res) - elif opcode == 0x0D: #tAreaN - not_in_name_formula(op, oname) - # res = get_cell_range_addr(data, pos+1, bv, reldelta=1) - # # note *ALL* tAreaN usage has signed offset for relative addresses - # any_rel = 1 - # if blah: print >> bk.logfile, " ", res - elif opcode == 0x1A: # tRef3d - if bv >= 80: - res = get_cell_addr(data, pos+3, bv, reldelta, browx, bcolx) - refx = unpack("<H", data[pos+1:pos+3])[0] - shx1, shx2 = get_externsheet_local_range(bk, refx, blah) - else: - res = get_cell_addr(data, pos+15, bv, reldelta, browx, bcolx) - raw_extshtx, raw_shx1, raw_shx2 = \ - unpack("<hxxxxxxxxhh", data[pos+1:pos+15]) - if blah: - print >> bk.logfile, "tRef3d", raw_extshtx, raw_shx1, raw_shx2 - shx1, shx2 = get_externsheet_local_range_b57( - bk, raw_extshtx, raw_shx1, raw_shx2, blah) - rowx, colx, row_rel, col_rel = res - is_rel = row_rel or col_rel - any_rel = any_rel or is_rel - coords = (shx1, shx2+1, rowx, rowx+1, colx, colx+1) - any_err |= shx1 < -1 - if blah: print >> bk.logfile, " ", coords - res = Operand(oUNK, None) - if is_rel: - relflags = (0, 0, row_rel, row_rel, col_rel, col_rel) - ref3d = Ref3D(coords + relflags) - res.kind = oREL - res.text = rangename3drel(bk, ref3d) - else: - ref3d = Ref3D(coords) - res.kind = oREF - res.text = rangename3d(bk, ref3d) - res.rank = LEAF_RANK - res.value = None - spush(res) - elif opcode == 0x1B: # tArea3d - if bv >= 80: - res1, res2 = get_cell_range_addr(data, pos+3, bv, reldelta) - refx = unpack("<H", data[pos+1:pos+3])[0] - shx1, shx2 = get_externsheet_local_range(bk, refx, blah) - else: - res1, res2 = get_cell_range_addr(data, pos+15, bv, reldelta) - raw_extshtx, raw_shx1, raw_shx2 = \ - unpack("<hxxxxxxxxhh", data[pos+1:pos+15]) - if blah: - print >> bk.logfile, "tArea3d", raw_extshtx, raw_shx1, raw_shx2 - shx1, shx2 = get_externsheet_local_range_b57( - bk, raw_extshtx, raw_shx1, raw_shx2, blah) - any_err |= shx1 < -1 - rowx1, colx1, row_rel1, col_rel1 = res1 - rowx2, colx2, row_rel2, col_rel2 = res2 - is_rel = row_rel1 or col_rel1 or row_rel2 or col_rel2 - any_rel = any_rel or is_rel - coords = (shx1, shx2+1, rowx1, rowx2+1, colx1, colx2+1) - if blah: print >> bk.logfile, " ", coords - res = Operand(oUNK, None) - if is_rel: - relflags = (0, 0, row_rel1, row_rel2, col_rel1, col_rel2) - ref3d = Ref3D(coords + relflags) - res.kind = oREL - res.text = rangename3drel(bk, ref3d) - else: - ref3d = Ref3D(coords) - res.kind = oREF - res.text = rangename3d(bk, ref3d) - res.rank = LEAF_RANK - spush(res) - elif opcode == 0x19: # tNameX - dodgy = 0 - res = Operand(oUNK, None) - if bv >= 80: - refx, tgtnamex = unpack("<HH", data[pos+1:pos+5]) - tgtnamex -= 1 - origrefx = refx - else: - refx, tgtnamex = unpack("<hxxxxxxxxH", data[pos+1:pos+13]) - tgtnamex -= 1 - origrefx = refx - if refx > 0: - refx -= 1 - elif refx < 0: - refx = -refx - 1 - else: - dodgy = 1 - if blah: - print >> bk.logfile, \ - " origrefx=%d refx=%d tgtnamex=%d dodgy=%d" \ - % (origrefx, refx, tgtnamex, dodgy) - # if tgtnamex == namex: - # if blah: print >> bk.logfile, "!!!! Self-referential !!!!" - # dodgy = any_err = 1 - if not dodgy: - if bv >= 80: - shx1, shx2 = get_externsheet_local_range(bk, refx, blah) - elif origrefx > 0: - shx1, shx2 = (-4, -4) # external ref - else: - exty = bk._externsheet_type_b57[refx] - if exty == 4: # non-specific sheet in own doc't - shx1, shx2 = (-1, -1) # internal, any sheet - else: - shx1, shx2 = (-666, -666) - okind = oUNK - ovalue = None - if shx1 == -5: # addin func name - okind = oSTRG - ovalue = bk.addin_func_names[tgtnamex] - otext = '"' + ovalue.replace('"', '""') + '"' - elif dodgy or shx1 < -1: - otext = "<<Name #%d in external(?) file #%d>>" \ - % (tgtnamex, origrefx) - else: - tgtobj = bk.name_obj_list[tgtnamex] - if tgtobj.scope == -1: - otext = tgtobj.name - else: - otext = "%s!%s" \ - % (bk._sheet_names[tgtobj.scope], tgtobj.name) - if blah: - print >> bk.logfile, " tNameX: setting text to", repr(res.text) - res = Operand(okind, ovalue, LEAF_RANK, otext) - spush(res) - elif is_error_opcode(opcode): - any_err = 1 - spush(error_opnd) - else: - if blah: - print >> bk.logfile, "FORMULA: /// Not handled yet: t" + oname - any_err = 1 - if sz <= 0: - raise FormulaError("Fatal: token size is not positive") - pos += sz - any_rel = not not any_rel - if blah: - print "End of formula. level=%d any_rel=%d any_err=%d stack=%r" % \ - (level, not not any_rel, any_err, stack) - if len(stack) >= 2: - print "*** Stack has unprocessed args" - print - - if len(stack) != 1: - result = None - else: - result = stack[0].text - return result - -#### under deconstruction ### -def dump_formula(bk, data, fmlalen, bv, reldelta, blah=0, isname=0): - if blah: - print "dump_formula", fmlalen, bv, len(data) - hex_char_dump(data, 0, fmlalen) - assert bv >= 80 #### this function needs updating #### - sztab = szdict[bv] - pos = 0 - stack = [] - any_rel = 0 - any_err = 0 - spush = stack.append - while 0 <= pos < fmlalen: - op = ord(data[pos]) - opcode = op & 0x1f - optype = (op & 0x60) >> 5 - if optype: - opx = opcode + 32 - else: - opx = opcode - oname = onames[opx] # + [" RVA"][optype] - - sz = sztab[opx] - if blah: - print "Pos:%d Op:0x%02x Name:t%s Sz:%d opcode:%02xh optype:%02xh" \ - % (pos, op, oname, sz, opcode, optype) - if not optype: - if 0x01 <= opcode <= 0x02: # tExp, tTbl - # reference to a shared formula or table record - rowx, colx = unpack("<HH", data[pos+1:pos+5]) - if blah: print >> bk.logfile, " ", (rowx, colx) - elif opcode == 0x10: # tList - if blah: print >> bk.logfile, "tList pre", stack - assert len(stack) >= 2 - bop = stack.pop() - aop = stack.pop() - spush(aop + bop) - if blah: print >> bk.logfile, "tlist post", stack - elif opcode == 0x11: # tRange - if blah: print >> bk.logfile, "tRange pre", stack - assert len(stack) >= 2 - bop = stack.pop() - aop = stack.pop() - assert len(aop) == 1 - assert len(bop) == 1 - result = do_box_funcs(tRangeFuncs, aop[0], bop[0]) - spush(result) - if blah: print >> bk.logfile, "tRange post", stack - elif opcode == 0x0F: # tIsect - if blah: print >> bk.logfile, "tIsect pre", stack - assert len(stack) >= 2 - bop = stack.pop() - aop = stack.pop() - assert len(aop) == 1 - assert len(bop) == 1 - result = do_box_funcs(tIsectFuncs, aop[0], bop[0]) - spush(result) - if blah: print >> bk.logfile, "tIsect post", stack - elif opcode == 0x19: # tAttr - subop, nc = unpack("<BH", data[pos+1:pos+4]) - subname = tAttrNames.get(subop, "??Unknown??") - if subop == 0x04: # Choose - sz = nc * 2 + 6 - else: - sz = 4 - if blah: print >> bk.logfile, " subop=%02xh subname=t%s sz=%d nc=%02xh" % (subop, subname, sz, nc) - elif opcode == 0x17: # tStr - if bv <= 70: - nc = ord(data[pos+1]) - strg = data[pos+2:pos+2+nc] # left in 8-bit encoding - sz = nc + 2 - else: - strg, newpos = unpack_unicode_update_pos(data, pos+1, lenlen=1) - sz = newpos - pos - if blah: print >> bk.logfile, " sz=%d strg=%r" % (sz, strg) - else: - if sz <= 0: - print "**** Dud size; exiting ****" - return - pos += sz - continue - if opcode == 0x00: # tArray - pass - elif opcode == 0x01: # tFunc - nb = 1 + int(bv >= 40) - funcx = unpack("<" + " BH"[nb], data[pos+1:pos+1+nb]) - if blah: print >> bk.logfile, " FuncID=%d" % funcx - elif opcode == 0x02: #tFuncVar - nb = 1 + int(bv >= 40) - nargs, funcx = unpack("<B" + " BH"[nb], data[pos+1:pos+2+nb]) - prompt, nargs = divmod(nargs, 128) - macro, funcx = divmod(funcx, 32768) - if blah: print >> bk.logfile, " FuncID=%d nargs=%d macro=%d prompt=%d" % (funcx, nargs, macro, prompt) - elif opcode == 0x03: #tName - namex = unpack("<H", data[pos+1:pos+3]) - # Only change with BIFF version is the number of trailing UNUSED bytes!!! - if blah: print >> bk.logfile, " namex=%d" % namex - elif opcode == 0x04: # tRef - res = get_cell_addr(data, pos+1, bv, reldelta) - if blah: print >> bk.logfile, " ", res - elif opcode == 0x05: # tArea - res = get_cell_range_addr(data, pos+1, bv, reldelta) - if blah: print >> bk.logfile, " ", res - elif opcode == 0x09: # tMemFunc - nb = unpack("<H", data[pos+1:pos+3])[0] - if blah: print >> bk.logfile, " %d bytes of cell ref formula" % nb - elif opcode == 0x0C: #tRefN - res = get_cell_addr(data, pos+1, bv, reldelta=1) - # note *ALL* tRefN usage has signed offset for relative addresses - any_rel = 1 - if blah: print >> bk.logfile, " ", res - elif opcode == 0x0D: #tAreaN - res = get_cell_range_addr(data, pos+1, bv, reldelta=1) - # note *ALL* tAreaN usage has signed offset for relative addresses - any_rel = 1 - if blah: print >> bk.logfile, " ", res - elif opcode == 0x1A: # tRef3d - refx = unpack("<H", data[pos+1:pos+3])[0] - res = get_cell_addr(data, pos+3, bv, reldelta) - if blah: print >> bk.logfile, " ", refx, res - rowx, colx, row_rel, col_rel = res - any_rel = any_rel or row_rel or col_rel - shx1, shx2 = get_externsheet_local_range(bk, refx, blah) - any_err |= shx1 < -1 - coords = (shx1, shx2+1, rowx, rowx+1, colx, colx+1) - if blah: print >> bk.logfile, " ", coords - if optype == 1: spush([coords]) - elif opcode == 0x1B: # tArea3d - refx = unpack("<H", data[pos+1:pos+3])[0] - res1, res2 = get_cell_range_addr(data, pos+3, bv, reldelta) - if blah: print >> bk.logfile, " ", refx, res1, res2 - rowx1, colx1, row_rel1, col_rel1 = res1 - rowx2, colx2, row_rel2, col_rel2 = res2 - any_rel = any_rel or row_rel1 or col_rel1 or row_rel2 or col_rel2 - shx1, shx2 = get_externsheet_local_range(bk, refx, blah) - any_err |= shx1 < -1 - coords = (shx1, shx2+1, rowx1, rowx2+1, colx1, colx2+1) - if blah: print >> bk.logfile, " ", coords - if optype == 1: spush([coords]) - elif opcode == 0x19: # tNameX - refx, namex = unpack("<HH", data[pos+1:pos+5]) - if blah: print >> bk.logfile, " refx=%d namex=%d" % (refx, namex) - elif is_error_opcode(opcode): - any_err = 1 - else: - if blah: print >> bk.logfile, "FORMULA: /// Not handled yet: t" + oname - any_err = 1 - if sz <= 0: - print "**** Dud size; exiting ****" - return - pos += sz - if blah: - print >> bk.logfile, "End of formula. any_rel=%d any_err=%d stack=%r" % \ - (not not any_rel, any_err, stack) - if len(stack) >= 2: - print >> bk.logfile, "*** Stack has unprocessed args" - -# === Some helper functions for displaying cell references === - -# Note that a "non-standard" syntax is used in row and column -# components in relative references. -# For example, consider a relative reference: up two rows, right 3 columns. -# On screen, with cursor in cell D10, this would appear as G8. -# On screen, with cursor in cell Z100, this would appear as AC98. -# On screen, with cursor in cell A1, this would appear as D65535. -# These functions will display such a reference as [@+3,#-2]. -# "@" refers to the unknown base column. -# "#" refers to the unknown base row. -# -# I'm aware of only one possibility of a sheet-relative component in -# a reference: a 2D reference located in the "current sheet". -# xlrd stores this internally with bounds of (0, 1, ...) and -# relative flags of (1, 1, ...). These functions display the -# sheet component as empty, just like Excel etc. - -def rownamerel(rowx, rowxrel): - if not rowxrel: - return "$%d" % rowx - if rowx > 0: - return "#+%d" % rowx - if rowx < 0: - return "#-%d" % (-rowx) - return "#" - -def colnamerel(colx, colxrel): - if not colxrel: - return "$" + colname(colx) - if colx > 0: - return "@+%d" % colx - if colx < 0: - return "@-%d" % (-colx) - return "@" -## -# Utility function: (5, 7) => 'H6' -def cellname(rowx, colx): - """ (5, 7) => 'H6' """ - return "%s%d" % (colname(colx), rowx+1) - -## -# Utility function: (5, 7) => '$H$6' -def cellnameabs(rowx, colx): - """ (5, 7) => '$H$6' """ - return "$%s$%d" % (colname(colx), rowx+1) - -def cellnamerel(rowx, colx, rowxrel, colxrel): - if not rowxrel and not colxrel: - return cellnameabs(rowx, colx) - return "[%s,%s]" % ( - colnamerel(colx, colxrel), - rownamerel(rowx, rowxrel)) -## -# Utility function: 7 => 'H', 27 => 'AB' -def colname(colx): - """ 7 => 'H', 27 => 'AB' """ - alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - if colx <= 25: - return alphabet[colx] - else: - xdiv26, xmod26 = divmod(colx, 26) - return alphabet[xdiv26 - 1] + alphabet[xmod26] - -def rangename2d(rlo, rhi, clo, chi): - """ (5, 20, 7, 10) => '$H$6:$J$20' """ - if rhi == rlo+1 and chi == clo+1: - return cellnameabs(rlo, clo) - return "%s:%s" % (cellnameabs(rlo, clo), cellnameabs(rhi-1, chi-1)) - -def rangename2drel((rlo, rhi, clo, chi), (rlorel, rhirel, clorel, chirel)): - return "%s:%s" % ( - cellnamerel(rlo, clo, rlorel, clorel), - cellnamerel(rhi-1, chi-1, rhirel, chirel) - ) -## -# Utility function: -# <br /> Ref3D((1, 4, 5, 20, 7, 10)) => 'Sheet2:Sheet3!$H$6:$J$20' -def rangename3d(book, ref3d): - """ Ref3D(1, 4, 5, 20, 7, 10) => 'Sheet2:Sheet3!$H$6:$J$20' - (assuming Excel's default sheetnames) """ - coords = ref3d.coords - return "%s!%s" % ( - sheetrange(book, *coords[:2]), - rangename2d(*coords[2:6])) - -## -# Utility function: -# <br /> Ref3D(coords=(0, 1, -32, -22, -13, 13), relflags=(0, 0, 1, 1, 1, 1)) -# => 'Sheet1![@-13,#-32]:[@+12,#-23]' -# where '@' refers to the current or base column and '#' -# refers to the current or base row. -def rangename3drel(book, ref3d): - coords = ref3d.coords - relflags = ref3d.relflags - shdesc = sheetrangerel(book, coords[:2], relflags[:2]) - rngdesc = rangename2drel(coords[2:6], relflags[2:6]) - if not shdesc: - return rngdesc - return "%s!%s" % (shdesc, rngdesc) - -def quotedsheetname(shnames, shx): - if shx >= 0: - shname = shnames[shx] - else: - shname = { - -1: "?internal; any sheet?", - -2: "internal; deleted sheet", - -3: "internal; macro sheet", - -4: "<<external>>", - }.get(shx, "?error %d?" % shx) - if "'" in shname: - return "'" + shname.replace("'", "''") + "'" - if " " in shname: - return "'" + shname + "'" - return shname - -def sheetrange(book, slo, shi): - shnames = book.sheet_names() - shdesc = quotedsheetname(shnames, slo) - if slo != shi-1: - shdesc += ":" + quotedsheetname(shnames, shi-1) - return shdesc - -def sheetrangerel(book, (slo, shi), (slorel, shirel)): - if not slorel and not shirel: - return sheetrange(book, slo, shi) - assert (slo == 0 == shi-1) and slorel and shirel - return "" - -# ============================================================== |
