formula.py 92 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179
  1. # -*- coding: cp1252 -*-
  2. ##
  3. # Module for parsing/evaluating Microsoft Excel formulas.
  4. #
  5. # <p>Copyright © 2005-2012 Stephen John Machin, Lingfo Pty Ltd</p>
  6. # <p>This module is part of the xlrd package, which is released under
  7. # a BSD-style licence.</p>
  8. ##
  9. # No part of the content of this file was derived from the works of David Giffin.
  10. from __future__ import print_function
  11. import copy
  12. from struct import unpack
  13. from .timemachine import *
  14. from .biffh import unpack_unicode_update_pos, unpack_string_update_pos, \
  15. XLRDError, hex_char_dump, error_text_from_code, BaseObject
  16. __all__ = [
  17. 'oBOOL', 'oERR', 'oNUM', 'oREF', 'oREL', 'oSTRG', 'oUNK',
  18. 'decompile_formula',
  19. 'dump_formula',
  20. 'evaluate_name_formula',
  21. 'okind_dict',
  22. 'rangename3d', 'rangename3drel', 'cellname', 'cellnameabs', 'colname',
  23. 'FMLA_TYPE_CELL',
  24. 'FMLA_TYPE_SHARED',
  25. 'FMLA_TYPE_ARRAY',
  26. 'FMLA_TYPE_COND_FMT',
  27. 'FMLA_TYPE_DATA_VAL',
  28. 'FMLA_TYPE_NAME',
  29. ]
  30. FMLA_TYPE_CELL = 1
  31. FMLA_TYPE_SHARED = 2
  32. FMLA_TYPE_ARRAY = 4
  33. FMLA_TYPE_COND_FMT = 8
  34. FMLA_TYPE_DATA_VAL = 16
  35. FMLA_TYPE_NAME = 32
  36. ALL_FMLA_TYPES = 63
  37. FMLA_TYPEDESCR_MAP = {
  38. 1 : 'CELL',
  39. 2 : 'SHARED',
  40. 4 : 'ARRAY',
  41. 8 : 'COND-FMT',
  42. 16: 'DATA-VAL',
  43. 32: 'NAME',
  44. }
  45. _TOKEN_NOT_ALLOWED = {
  46. 0x01: ALL_FMLA_TYPES - FMLA_TYPE_CELL, # tExp
  47. 0x02: ALL_FMLA_TYPES - FMLA_TYPE_CELL, # tTbl
  48. 0x0F: FMLA_TYPE_SHARED + FMLA_TYPE_COND_FMT + FMLA_TYPE_DATA_VAL, # tIsect
  49. 0x10: FMLA_TYPE_SHARED + FMLA_TYPE_COND_FMT + FMLA_TYPE_DATA_VAL, # tUnion/List
  50. 0x11: FMLA_TYPE_SHARED + FMLA_TYPE_COND_FMT + FMLA_TYPE_DATA_VAL, # tRange
  51. 0x20: FMLA_TYPE_SHARED + FMLA_TYPE_COND_FMT + FMLA_TYPE_DATA_VAL, # tArray
  52. 0x23: FMLA_TYPE_SHARED, # tName
  53. 0x39: FMLA_TYPE_SHARED + FMLA_TYPE_COND_FMT + FMLA_TYPE_DATA_VAL, # tNameX
  54. 0x3A: FMLA_TYPE_SHARED + FMLA_TYPE_COND_FMT + FMLA_TYPE_DATA_VAL, # tRef3d
  55. 0x3B: FMLA_TYPE_SHARED + FMLA_TYPE_COND_FMT + FMLA_TYPE_DATA_VAL, # tArea3d
  56. 0x2C: FMLA_TYPE_CELL + FMLA_TYPE_ARRAY, # tRefN
  57. 0x2D: FMLA_TYPE_CELL + FMLA_TYPE_ARRAY, # tAreaN
  58. # plus weird stuff like tMem*
  59. }.get
  60. oBOOL = 3
  61. oERR = 4
  62. oMSNG = 5 # tMissArg
  63. oNUM = 2
  64. oREF = -1
  65. oREL = -2
  66. oSTRG = 1
  67. oUNK = 0
  68. okind_dict = {
  69. -2: "oREL",
  70. -1: "oREF",
  71. 0 : "oUNK",
  72. 1 : "oSTRG",
  73. 2 : "oNUM",
  74. 3 : "oBOOL",
  75. 4 : "oERR",
  76. 5 : "oMSNG",
  77. }
  78. listsep = ',' #### probably should depend on locale
  79. # sztabN[opcode] -> the number of bytes to consume.
  80. # -1 means variable
  81. # -2 means this opcode not implemented in this version.
  82. # Which N to use? Depends on biff_version; see szdict.
  83. 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]
  84. 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]
  85. 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]
  86. 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]
  87. 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]
  88. szdict = {
  89. 20 : sztab0,
  90. 21 : sztab0,
  91. 30 : sztab1,
  92. 40 : sztab2,
  93. 45 : sztab2,
  94. 50 : sztab3,
  95. 70 : sztab3,
  96. 80 : sztab4,
  97. }
  98. # For debugging purposes ... the name for each opcode
  99. # (without the prefix "t" used on OOo docs)
  100. 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', '', '']
  101. func_defs = {
  102. # index: (name, min#args, max#args, flags, #known_args, return_type, kargs)
  103. 0 : ('COUNT', 0, 30, 0x04, 1, 'V', 'R'),
  104. 1 : ('IF', 2, 3, 0x04, 3, 'V', 'VRR'),
  105. 2 : ('ISNA', 1, 1, 0x02, 1, 'V', 'V'),
  106. 3 : ('ISERROR', 1, 1, 0x02, 1, 'V', 'V'),
  107. 4 : ('SUM', 0, 30, 0x04, 1, 'V', 'R'),
  108. 5 : ('AVERAGE', 1, 30, 0x04, 1, 'V', 'R'),
  109. 6 : ('MIN', 1, 30, 0x04, 1, 'V', 'R'),
  110. 7 : ('MAX', 1, 30, 0x04, 1, 'V', 'R'),
  111. 8 : ('ROW', 0, 1, 0x04, 1, 'V', 'R'),
  112. 9 : ('COLUMN', 0, 1, 0x04, 1, 'V', 'R'),
  113. 10 : ('NA', 0, 0, 0x02, 0, 'V', ''),
  114. 11 : ('NPV', 2, 30, 0x04, 2, 'V', 'VR'),
  115. 12 : ('STDEV', 1, 30, 0x04, 1, 'V', 'R'),
  116. 13 : ('DOLLAR', 1, 2, 0x04, 1, 'V', 'V'),
  117. 14 : ('FIXED', 2, 3, 0x04, 3, 'V', 'VVV'),
  118. 15 : ('SIN', 1, 1, 0x02, 1, 'V', 'V'),
  119. 16 : ('COS', 1, 1, 0x02, 1, 'V', 'V'),
  120. 17 : ('TAN', 1, 1, 0x02, 1, 'V', 'V'),
  121. 18 : ('ATAN', 1, 1, 0x02, 1, 'V', 'V'),
  122. 19 : ('PI', 0, 0, 0x02, 0, 'V', ''),
  123. 20 : ('SQRT', 1, 1, 0x02, 1, 'V', 'V'),
  124. 21 : ('EXP', 1, 1, 0x02, 1, 'V', 'V'),
  125. 22 : ('LN', 1, 1, 0x02, 1, 'V', 'V'),
  126. 23 : ('LOG10', 1, 1, 0x02, 1, 'V', 'V'),
  127. 24 : ('ABS', 1, 1, 0x02, 1, 'V', 'V'),
  128. 25 : ('INT', 1, 1, 0x02, 1, 'V', 'V'),
  129. 26 : ('SIGN', 1, 1, 0x02, 1, 'V', 'V'),
  130. 27 : ('ROUND', 2, 2, 0x02, 2, 'V', 'VV'),
  131. 28 : ('LOOKUP', 2, 3, 0x04, 2, 'V', 'VR'),
  132. 29 : ('INDEX', 2, 4, 0x0c, 4, 'R', 'RVVV'),
  133. 30 : ('REPT', 2, 2, 0x02, 2, 'V', 'VV'),
  134. 31 : ('MID', 3, 3, 0x02, 3, 'V', 'VVV'),
  135. 32 : ('LEN', 1, 1, 0x02, 1, 'V', 'V'),
  136. 33 : ('VALUE', 1, 1, 0x02, 1, 'V', 'V'),
  137. 34 : ('TRUE', 0, 0, 0x02, 0, 'V', ''),
  138. 35 : ('FALSE', 0, 0, 0x02, 0, 'V', ''),
  139. 36 : ('AND', 1, 30, 0x04, 1, 'V', 'R'),
  140. 37 : ('OR', 1, 30, 0x04, 1, 'V', 'R'),
  141. 38 : ('NOT', 1, 1, 0x02, 1, 'V', 'V'),
  142. 39 : ('MOD', 2, 2, 0x02, 2, 'V', 'VV'),
  143. 40 : ('DCOUNT', 3, 3, 0x02, 3, 'V', 'RRR'),
  144. 41 : ('DSUM', 3, 3, 0x02, 3, 'V', 'RRR'),
  145. 42 : ('DAVERAGE', 3, 3, 0x02, 3, 'V', 'RRR'),
  146. 43 : ('DMIN', 3, 3, 0x02, 3, 'V', 'RRR'),
  147. 44 : ('DMAX', 3, 3, 0x02, 3, 'V', 'RRR'),
  148. 45 : ('DSTDEV', 3, 3, 0x02, 3, 'V', 'RRR'),
  149. 46 : ('VAR', 1, 30, 0x04, 1, 'V', 'R'),
  150. 47 : ('DVAR', 3, 3, 0x02, 3, 'V', 'RRR'),
  151. 48 : ('TEXT', 2, 2, 0x02, 2, 'V', 'VV'),
  152. 49 : ('LINEST', 1, 4, 0x04, 4, 'A', 'RRVV'),
  153. 50 : ('TREND', 1, 4, 0x04, 4, 'A', 'RRRV'),
  154. 51 : ('LOGEST', 1, 4, 0x04, 4, 'A', 'RRVV'),
  155. 52 : ('GROWTH', 1, 4, 0x04, 4, 'A', 'RRRV'),
  156. 56 : ('PV', 3, 5, 0x04, 5, 'V', 'VVVVV'),
  157. 57 : ('FV', 3, 5, 0x04, 5, 'V', 'VVVVV'),
  158. 58 : ('NPER', 3, 5, 0x04, 5, 'V', 'VVVVV'),
  159. 59 : ('PMT', 3, 5, 0x04, 5, 'V', 'VVVVV'),
  160. 60 : ('RATE', 3, 6, 0x04, 6, 'V', 'VVVVVV'),
  161. 61 : ('MIRR', 3, 3, 0x02, 3, 'V', 'RVV'),
  162. 62 : ('IRR', 1, 2, 0x04, 2, 'V', 'RV'),
  163. 63 : ('RAND', 0, 0, 0x0a, 0, 'V', ''),
  164. 64 : ('MATCH', 2, 3, 0x04, 3, 'V', 'VRR'),
  165. 65 : ('DATE', 3, 3, 0x02, 3, 'V', 'VVV'),
  166. 66 : ('TIME', 3, 3, 0x02, 3, 'V', 'VVV'),
  167. 67 : ('DAY', 1, 1, 0x02, 1, 'V', 'V'),
  168. 68 : ('MONTH', 1, 1, 0x02, 1, 'V', 'V'),
  169. 69 : ('YEAR', 1, 1, 0x02, 1, 'V', 'V'),
  170. 70 : ('WEEKDAY', 1, 2, 0x04, 2, 'V', 'VV'),
  171. 71 : ('HOUR', 1, 1, 0x02, 1, 'V', 'V'),
  172. 72 : ('MINUTE', 1, 1, 0x02, 1, 'V', 'V'),
  173. 73 : ('SECOND', 1, 1, 0x02, 1, 'V', 'V'),
  174. 74 : ('NOW', 0, 0, 0x0a, 0, 'V', ''),
  175. 75 : ('AREAS', 1, 1, 0x02, 1, 'V', 'R'),
  176. 76 : ('ROWS', 1, 1, 0x02, 1, 'V', 'R'),
  177. 77 : ('COLUMNS', 1, 1, 0x02, 1, 'V', 'R'),
  178. 78 : ('OFFSET', 3, 5, 0x04, 5, 'R', 'RVVVV'),
  179. 82 : ('SEARCH', 2, 3, 0x04, 3, 'V', 'VVV'),
  180. 83 : ('TRANSPOSE', 1, 1, 0x02, 1, 'A', 'A'),
  181. 86 : ('TYPE', 1, 1, 0x02, 1, 'V', 'V'),
  182. 92 : ('SERIESSUM', 4, 4, 0x02, 4, 'V', 'VVVA'),
  183. 97 : ('ATAN2', 2, 2, 0x02, 2, 'V', 'VV'),
  184. 98 : ('ASIN', 1, 1, 0x02, 1, 'V', 'V'),
  185. 99 : ('ACOS', 1, 1, 0x02, 1, 'V', 'V'),
  186. 100: ('CHOOSE', 2, 30, 0x04, 2, 'V', 'VR'),
  187. 101: ('HLOOKUP', 3, 4, 0x04, 4, 'V', 'VRRV'),
  188. 102: ('VLOOKUP', 3, 4, 0x04, 4, 'V', 'VRRV'),
  189. 105: ('ISREF', 1, 1, 0x02, 1, 'V', 'R'),
  190. 109: ('LOG', 1, 2, 0x04, 2, 'V', 'VV'),
  191. 111: ('CHAR', 1, 1, 0x02, 1, 'V', 'V'),
  192. 112: ('LOWER', 1, 1, 0x02, 1, 'V', 'V'),
  193. 113: ('UPPER', 1, 1, 0x02, 1, 'V', 'V'),
  194. 114: ('PROPER', 1, 1, 0x02, 1, 'V', 'V'),
  195. 115: ('LEFT', 1, 2, 0x04, 2, 'V', 'VV'),
  196. 116: ('RIGHT', 1, 2, 0x04, 2, 'V', 'VV'),
  197. 117: ('EXACT', 2, 2, 0x02, 2, 'V', 'VV'),
  198. 118: ('TRIM', 1, 1, 0x02, 1, 'V', 'V'),
  199. 119: ('REPLACE', 4, 4, 0x02, 4, 'V', 'VVVV'),
  200. 120: ('SUBSTITUTE', 3, 4, 0x04, 4, 'V', 'VVVV'),
  201. 121: ('CODE', 1, 1, 0x02, 1, 'V', 'V'),
  202. 124: ('FIND', 2, 3, 0x04, 3, 'V', 'VVV'),
  203. 125: ('CELL', 1, 2, 0x0c, 2, 'V', 'VR'),
  204. 126: ('ISERR', 1, 1, 0x02, 1, 'V', 'V'),
  205. 127: ('ISTEXT', 1, 1, 0x02, 1, 'V', 'V'),
  206. 128: ('ISNUMBER', 1, 1, 0x02, 1, 'V', 'V'),
  207. 129: ('ISBLANK', 1, 1, 0x02, 1, 'V', 'V'),
  208. 130: ('T', 1, 1, 0x02, 1, 'V', 'R'),
  209. 131: ('N', 1, 1, 0x02, 1, 'V', 'R'),
  210. 140: ('DATEVALUE', 1, 1, 0x02, 1, 'V', 'V'),
  211. 141: ('TIMEVALUE', 1, 1, 0x02, 1, 'V', 'V'),
  212. 142: ('SLN', 3, 3, 0x02, 3, 'V', 'VVV'),
  213. 143: ('SYD', 4, 4, 0x02, 4, 'V', 'VVVV'),
  214. 144: ('DDB', 4, 5, 0x04, 5, 'V', 'VVVVV'),
  215. 148: ('INDIRECT', 1, 2, 0x0c, 2, 'R', 'VV'),
  216. 162: ('CLEAN', 1, 1, 0x02, 1, 'V', 'V'),
  217. 163: ('MDETERM', 1, 1, 0x02, 1, 'V', 'A'),
  218. 164: ('MINVERSE', 1, 1, 0x02, 1, 'A', 'A'),
  219. 165: ('MMULT', 2, 2, 0x02, 2, 'A', 'AA'),
  220. 167: ('IPMT', 4, 6, 0x04, 6, 'V', 'VVVVVV'),
  221. 168: ('PPMT', 4, 6, 0x04, 6, 'V', 'VVVVVV'),
  222. 169: ('COUNTA', 0, 30, 0x04, 1, 'V', 'R'),
  223. 183: ('PRODUCT', 0, 30, 0x04, 1, 'V', 'R'),
  224. 184: ('FACT', 1, 1, 0x02, 1, 'V', 'V'),
  225. 189: ('DPRODUCT', 3, 3, 0x02, 3, 'V', 'RRR'),
  226. 190: ('ISNONTEXT', 1, 1, 0x02, 1, 'V', 'V'),
  227. 193: ('STDEVP', 1, 30, 0x04, 1, 'V', 'R'),
  228. 194: ('VARP', 1, 30, 0x04, 1, 'V', 'R'),
  229. 195: ('DSTDEVP', 3, 3, 0x02, 3, 'V', 'RRR'),
  230. 196: ('DVARP', 3, 3, 0x02, 3, 'V', 'RRR'),
  231. 197: ('TRUNC', 1, 2, 0x04, 2, 'V', 'VV'),
  232. 198: ('ISLOGICAL', 1, 1, 0x02, 1, 'V', 'V'),
  233. 199: ('DCOUNTA', 3, 3, 0x02, 3, 'V', 'RRR'),
  234. 204: ('USDOLLAR', 1, 2, 0x04, 2, 'V', 'VV'),
  235. 205: ('FINDB', 2, 3, 0x04, 3, 'V', 'VVV'),
  236. 206: ('SEARCHB', 2, 3, 0x04, 3, 'V', 'VVV'),
  237. 207: ('REPLACEB', 4, 4, 0x02, 4, 'V', 'VVVV'),
  238. 208: ('LEFTB', 1, 2, 0x04, 2, 'V', 'VV'),
  239. 209: ('RIGHTB', 1, 2, 0x04, 2, 'V', 'VV'),
  240. 210: ('MIDB', 3, 3, 0x02, 3, 'V', 'VVV'),
  241. 211: ('LENB', 1, 1, 0x02, 1, 'V', 'V'),
  242. 212: ('ROUNDUP', 2, 2, 0x02, 2, 'V', 'VV'),
  243. 213: ('ROUNDDOWN', 2, 2, 0x02, 2, 'V', 'VV'),
  244. 214: ('ASC', 1, 1, 0x02, 1, 'V', 'V'),
  245. 215: ('DBCS', 1, 1, 0x02, 1, 'V', 'V'),
  246. 216: ('RANK', 2, 3, 0x04, 3, 'V', 'VRV'),
  247. 219: ('ADDRESS', 2, 5, 0x04, 5, 'V', 'VVVVV'),
  248. 220: ('DAYS360', 2, 3, 0x04, 3, 'V', 'VVV'),
  249. 221: ('TODAY', 0, 0, 0x0a, 0, 'V', ''),
  250. 222: ('VDB', 5, 7, 0x04, 7, 'V', 'VVVVVVV'),
  251. 227: ('MEDIAN', 1, 30, 0x04, 1, 'V', 'R'),
  252. 228: ('SUMPRODUCT', 1, 30, 0x04, 1, 'V', 'A'),
  253. 229: ('SINH', 1, 1, 0x02, 1, 'V', 'V'),
  254. 230: ('COSH', 1, 1, 0x02, 1, 'V', 'V'),
  255. 231: ('TANH', 1, 1, 0x02, 1, 'V', 'V'),
  256. 232: ('ASINH', 1, 1, 0x02, 1, 'V', 'V'),
  257. 233: ('ACOSH', 1, 1, 0x02, 1, 'V', 'V'),
  258. 234: ('ATANH', 1, 1, 0x02, 1, 'V', 'V'),
  259. 235: ('DGET', 3, 3, 0x02, 3, 'V', 'RRR'),
  260. 244: ('INFO', 1, 1, 0x02, 1, 'V', 'V'),
  261. 247: ('DB', 4, 5, 0x04, 5, 'V', 'VVVVV'),
  262. 252: ('FREQUENCY', 2, 2, 0x02, 2, 'A', 'RR'),
  263. 261: ('ERROR.TYPE', 1, 1, 0x02, 1, 'V', 'V'),
  264. 269: ('AVEDEV', 1, 30, 0x04, 1, 'V', 'R'),
  265. 270: ('BETADIST', 3, 5, 0x04, 1, 'V', 'V'),
  266. 271: ('GAMMALN', 1, 1, 0x02, 1, 'V', 'V'),
  267. 272: ('BETAINV', 3, 5, 0x04, 1, 'V', 'V'),
  268. 273: ('BINOMDIST', 4, 4, 0x02, 4, 'V', 'VVVV'),
  269. 274: ('CHIDIST', 2, 2, 0x02, 2, 'V', 'VV'),
  270. 275: ('CHIINV', 2, 2, 0x02, 2, 'V', 'VV'),
  271. 276: ('COMBIN', 2, 2, 0x02, 2, 'V', 'VV'),
  272. 277: ('CONFIDENCE', 3, 3, 0x02, 3, 'V', 'VVV'),
  273. 278: ('CRITBINOM', 3, 3, 0x02, 3, 'V', 'VVV'),
  274. 279: ('EVEN', 1, 1, 0x02, 1, 'V', 'V'),
  275. 280: ('EXPONDIST', 3, 3, 0x02, 3, 'V', 'VVV'),
  276. 281: ('FDIST', 3, 3, 0x02, 3, 'V', 'VVV'),
  277. 282: ('FINV', 3, 3, 0x02, 3, 'V', 'VVV'),
  278. 283: ('FISHER', 1, 1, 0x02, 1, 'V', 'V'),
  279. 284: ('FISHERINV', 1, 1, 0x02, 1, 'V', 'V'),
  280. 285: ('FLOOR', 2, 2, 0x02, 2, 'V', 'VV'),
  281. 286: ('GAMMADIST', 4, 4, 0x02, 4, 'V', 'VVVV'),
  282. 287: ('GAMMAINV', 3, 3, 0x02, 3, 'V', 'VVV'),
  283. 288: ('CEILING', 2, 2, 0x02, 2, 'V', 'VV'),
  284. 289: ('HYPGEOMDIST', 4, 4, 0x02, 4, 'V', 'VVVV'),
  285. 290: ('LOGNORMDIST', 3, 3, 0x02, 3, 'V', 'VVV'),
  286. 291: ('LOGINV', 3, 3, 0x02, 3, 'V', 'VVV'),
  287. 292: ('NEGBINOMDIST', 3, 3, 0x02, 3, 'V', 'VVV'),
  288. 293: ('NORMDIST', 4, 4, 0x02, 4, 'V', 'VVVV'),
  289. 294: ('NORMSDIST', 1, 1, 0x02, 1, 'V', 'V'),
  290. 295: ('NORMINV', 3, 3, 0x02, 3, 'V', 'VVV'),
  291. 296: ('NORMSINV', 1, 1, 0x02, 1, 'V', 'V'),
  292. 297: ('STANDARDIZE', 3, 3, 0x02, 3, 'V', 'VVV'),
  293. 298: ('ODD', 1, 1, 0x02, 1, 'V', 'V'),
  294. 299: ('PERMUT', 2, 2, 0x02, 2, 'V', 'VV'),
  295. 300: ('POISSON', 3, 3, 0x02, 3, 'V', 'VVV'),
  296. 301: ('TDIST', 3, 3, 0x02, 3, 'V', 'VVV'),
  297. 302: ('WEIBULL', 4, 4, 0x02, 4, 'V', 'VVVV'),
  298. 303: ('SUMXMY2', 2, 2, 0x02, 2, 'V', 'AA'),
  299. 304: ('SUMX2MY2', 2, 2, 0x02, 2, 'V', 'AA'),
  300. 305: ('SUMX2PY2', 2, 2, 0x02, 2, 'V', 'AA'),
  301. 306: ('CHITEST', 2, 2, 0x02, 2, 'V', 'AA'),
  302. 307: ('CORREL', 2, 2, 0x02, 2, 'V', 'AA'),
  303. 308: ('COVAR', 2, 2, 0x02, 2, 'V', 'AA'),
  304. 309: ('FORECAST', 3, 3, 0x02, 3, 'V', 'VAA'),
  305. 310: ('FTEST', 2, 2, 0x02, 2, 'V', 'AA'),
  306. 311: ('INTERCEPT', 2, 2, 0x02, 2, 'V', 'AA'),
  307. 312: ('PEARSON', 2, 2, 0x02, 2, 'V', 'AA'),
  308. 313: ('RSQ', 2, 2, 0x02, 2, 'V', 'AA'),
  309. 314: ('STEYX', 2, 2, 0x02, 2, 'V', 'AA'),
  310. 315: ('SLOPE', 2, 2, 0x02, 2, 'V', 'AA'),
  311. 316: ('TTEST', 4, 4, 0x02, 4, 'V', 'AAVV'),
  312. 317: ('PROB', 3, 4, 0x04, 3, 'V', 'AAV'),
  313. 318: ('DEVSQ', 1, 30, 0x04, 1, 'V', 'R'),
  314. 319: ('GEOMEAN', 1, 30, 0x04, 1, 'V', 'R'),
  315. 320: ('HARMEAN', 1, 30, 0x04, 1, 'V', 'R'),
  316. 321: ('SUMSQ', 0, 30, 0x04, 1, 'V', 'R'),
  317. 322: ('KURT', 1, 30, 0x04, 1, 'V', 'R'),
  318. 323: ('SKEW', 1, 30, 0x04, 1, 'V', 'R'),
  319. 324: ('ZTEST', 2, 3, 0x04, 2, 'V', 'RV'),
  320. 325: ('LARGE', 2, 2, 0x02, 2, 'V', 'RV'),
  321. 326: ('SMALL', 2, 2, 0x02, 2, 'V', 'RV'),
  322. 327: ('QUARTILE', 2, 2, 0x02, 2, 'V', 'RV'),
  323. 328: ('PERCENTILE', 2, 2, 0x02, 2, 'V', 'RV'),
  324. 329: ('PERCENTRANK', 2, 3, 0x04, 2, 'V', 'RV'),
  325. 330: ('MODE', 1, 30, 0x04, 1, 'V', 'A'),
  326. 331: ('TRIMMEAN', 2, 2, 0x02, 2, 'V', 'RV'),
  327. 332: ('TINV', 2, 2, 0x02, 2, 'V', 'VV'),
  328. 336: ('CONCATENATE', 0, 30, 0x04, 1, 'V', 'V'),
  329. 337: ('POWER', 2, 2, 0x02, 2, 'V', 'VV'),
  330. 342: ('RADIANS', 1, 1, 0x02, 1, 'V', 'V'),
  331. 343: ('DEGREES', 1, 1, 0x02, 1, 'V', 'V'),
  332. 344: ('SUBTOTAL', 2, 30, 0x04, 2, 'V', 'VR'),
  333. 345: ('SUMIF', 2, 3, 0x04, 3, 'V', 'RVR'),
  334. 346: ('COUNTIF', 2, 2, 0x02, 2, 'V', 'RV'),
  335. 347: ('COUNTBLANK', 1, 1, 0x02, 1, 'V', 'R'),
  336. 350: ('ISPMT', 4, 4, 0x02, 4, 'V', 'VVVV'),
  337. 351: ('DATEDIF', 3, 3, 0x02, 3, 'V', 'VVV'),
  338. 352: ('DATESTRING', 1, 1, 0x02, 1, 'V', 'V'),
  339. 353: ('NUMBERSTRING', 2, 2, 0x02, 2, 'V', 'VV'),
  340. 354: ('ROMAN', 1, 2, 0x04, 2, 'V', 'VV'),
  341. 358: ('GETPIVOTDATA', 2, 2, 0x02, 2, 'V', 'RV'),
  342. 359: ('HYPERLINK', 1, 2, 0x04, 2, 'V', 'VV'),
  343. 360: ('PHONETIC', 1, 1, 0x02, 1, 'V', 'V'),
  344. 361: ('AVERAGEA', 1, 30, 0x04, 1, 'V', 'R'),
  345. 362: ('MAXA', 1, 30, 0x04, 1, 'V', 'R'),
  346. 363: ('MINA', 1, 30, 0x04, 1, 'V', 'R'),
  347. 364: ('STDEVPA', 1, 30, 0x04, 1, 'V', 'R'),
  348. 365: ('VARPA', 1, 30, 0x04, 1, 'V', 'R'),
  349. 366: ('STDEVA', 1, 30, 0x04, 1, 'V', 'R'),
  350. 367: ('VARA', 1, 30, 0x04, 1, 'V', 'R'),
  351. 368: ('BAHTTEXT', 1, 1, 0x02, 1, 'V', 'V'),
  352. 369: ('THAIDAYOFWEEK', 1, 1, 0x02, 1, 'V', 'V'),
  353. 370: ('THAIDIGIT', 1, 1, 0x02, 1, 'V', 'V'),
  354. 371: ('THAIMONTHOFYEAR', 1, 1, 0x02, 1, 'V', 'V'),
  355. 372: ('THAINUMSOUND', 1, 1, 0x02, 1, 'V', 'V'),
  356. 373: ('THAINUMSTRING', 1, 1, 0x02, 1, 'V', 'V'),
  357. 374: ('THAISTRINGLENGTH', 1, 1, 0x02, 1, 'V', 'V'),
  358. 375: ('ISTHAIDIGIT', 1, 1, 0x02, 1, 'V', 'V'),
  359. 376: ('ROUNDBAHTDOWN', 1, 1, 0x02, 1, 'V', 'V'),
  360. 377: ('ROUNDBAHTUP', 1, 1, 0x02, 1, 'V', 'V'),
  361. 378: ('THAIYEAR', 1, 1, 0x02, 1, 'V', 'V'),
  362. 379: ('RTD', 2, 5, 0x04, 1, 'V', 'V'),
  363. }
  364. tAttrNames = {
  365. 0x00: "Skip??", # seen in SAMPLES.XLS which shipped with Excel 5.0
  366. 0x01: "Volatile",
  367. 0x02: "If",
  368. 0x04: "Choose",
  369. 0x08: "Skip",
  370. 0x10: "Sum",
  371. 0x20: "Assign",
  372. 0x40: "Space",
  373. 0x41: "SpaceVolatile",
  374. }
  375. error_opcodes = set([0x07, 0x08, 0x0A, 0x0B, 0x1C, 0x1D, 0x2F])
  376. tRangeFuncs = (min, max, min, max, min, max)
  377. tIsectFuncs = (max, min, max, min, max, min)
  378. def do_box_funcs(box_funcs, boxa, boxb):
  379. return tuple([
  380. func(numa, numb)
  381. for func, numa, numb in zip(box_funcs, boxa.coords, boxb.coords)
  382. ])
  383. def adjust_cell_addr_biff8(rowval, colval, reldelta, browx=None, bcolx=None):
  384. row_rel = (colval >> 15) & 1
  385. col_rel = (colval >> 14) & 1
  386. rowx = rowval
  387. colx = colval & 0xff
  388. if reldelta:
  389. if row_rel and rowx >= 32768:
  390. rowx -= 65536
  391. if col_rel and colx >= 128:
  392. colx -= 256
  393. else:
  394. if row_rel:
  395. rowx -= browx
  396. if col_rel:
  397. colx -= bcolx
  398. return rowx, colx, row_rel, col_rel
  399. def adjust_cell_addr_biff_le7(
  400. rowval, colval, reldelta, browx=None, bcolx=None):
  401. row_rel = (rowval >> 15) & 1
  402. col_rel = (rowval >> 14) & 1
  403. rowx = rowval & 0x3fff
  404. colx = colval
  405. if reldelta:
  406. if row_rel and rowx >= 8192:
  407. rowx -= 16384
  408. if col_rel and colx >= 128:
  409. colx -= 256
  410. else:
  411. if row_rel:
  412. rowx -= browx
  413. if col_rel:
  414. colx -= bcolx
  415. return rowx, colx, row_rel, col_rel
  416. def get_cell_addr(data, pos, bv, reldelta, browx=None, bcolx=None):
  417. if bv >= 80:
  418. rowval, colval = unpack("<HH", data[pos:pos+4])
  419. # print " rv=%04xh cv=%04xh" % (rowval, colval)
  420. return adjust_cell_addr_biff8(rowval, colval, reldelta, browx, bcolx)
  421. else:
  422. rowval, colval = unpack("<HB", data[pos:pos+3])
  423. # print " rv=%04xh cv=%04xh" % (rowval, colval)
  424. return adjust_cell_addr_biff_le7(
  425. rowval, colval, reldelta, browx, bcolx)
  426. def get_cell_range_addr(data, pos, bv, reldelta, browx=None, bcolx=None):
  427. if bv >= 80:
  428. row1val, row2val, col1val, col2val = unpack("<HHHH", data[pos:pos+8])
  429. # print " rv=%04xh cv=%04xh" % (row1val, col1val)
  430. # print " rv=%04xh cv=%04xh" % (row2val, col2val)
  431. res1 = adjust_cell_addr_biff8(row1val, col1val, reldelta, browx, bcolx)
  432. res2 = adjust_cell_addr_biff8(row2val, col2val, reldelta, browx, bcolx)
  433. return res1, res2
  434. else:
  435. row1val, row2val, col1val, col2val = unpack("<HHBB", data[pos:pos+6])
  436. # print " rv=%04xh cv=%04xh" % (row1val, col1val)
  437. # print " rv=%04xh cv=%04xh" % (row2val, col2val)
  438. res1 = adjust_cell_addr_biff_le7(
  439. row1val, col1val, reldelta, browx, bcolx)
  440. res2 = adjust_cell_addr_biff_le7(
  441. row2val, col2val, reldelta, browx, bcolx)
  442. return res1, res2
  443. def get_externsheet_local_range(bk, refx, blah=0):
  444. try:
  445. info = bk._externsheet_info[refx]
  446. except IndexError:
  447. print("!!! get_externsheet_local_range: refx=%d, not in range(%d)" \
  448. % (refx, len(bk._externsheet_info)), file=bk.logfile)
  449. return (-101, -101)
  450. ref_recordx, ref_first_sheetx, ref_last_sheetx = info
  451. if ref_recordx == bk._supbook_addins_inx:
  452. if blah:
  453. print("/// get_externsheet_local_range(refx=%d) -> addins %r" % (refx, info), file=bk.logfile)
  454. assert ref_first_sheetx == 0xFFFE == ref_last_sheetx
  455. return (-5, -5)
  456. if ref_recordx != bk._supbook_locals_inx:
  457. if blah:
  458. print("/// get_externsheet_local_range(refx=%d) -> external %r" % (refx, info), file=bk.logfile)
  459. return (-4, -4) # external reference
  460. if ref_first_sheetx == 0xFFFE == ref_last_sheetx:
  461. if blah:
  462. print("/// get_externsheet_local_range(refx=%d) -> unspecified sheet %r" % (refx, info), file=bk.logfile)
  463. return (-1, -1) # internal reference, any sheet
  464. if ref_first_sheetx == 0xFFFF == ref_last_sheetx:
  465. if blah:
  466. print("/// get_externsheet_local_range(refx=%d) -> deleted sheet(s)" % (refx, ), file=bk.logfile)
  467. return (-2, -2) # internal reference, deleted sheet(s)
  468. nsheets = len(bk._all_sheets_map)
  469. if not(0 <= ref_first_sheetx <= ref_last_sheetx < nsheets):
  470. if blah:
  471. print("/// get_externsheet_local_range(refx=%d) -> %r" % (refx, info), file=bk.logfile)
  472. print("--- first/last sheet not in range(%d)" % nsheets, file=bk.logfile)
  473. return (-102, -102) # stuffed up somewhere :-(
  474. xlrd_sheetx1 = bk._all_sheets_map[ref_first_sheetx]
  475. xlrd_sheetx2 = bk._all_sheets_map[ref_last_sheetx]
  476. if not(0 <= xlrd_sheetx1 <= xlrd_sheetx2):
  477. return (-3, -3) # internal reference, but to a macro sheet
  478. return xlrd_sheetx1, xlrd_sheetx2
  479. def get_externsheet_local_range_b57(
  480. bk, raw_extshtx, ref_first_sheetx, ref_last_sheetx, blah=0):
  481. if raw_extshtx > 0:
  482. if blah:
  483. print("/// get_externsheet_local_range_b57(raw_extshtx=%d) -> external" % raw_extshtx, file=bk.logfile)
  484. return (-4, -4) # external reference
  485. if ref_first_sheetx == -1 and ref_last_sheetx == -1:
  486. return (-2, -2) # internal reference, deleted sheet(s)
  487. nsheets = len(bk._all_sheets_map)
  488. if not(0 <= ref_first_sheetx <= ref_last_sheetx < nsheets):
  489. if blah:
  490. print("/// get_externsheet_local_range_b57(%d, %d, %d) -> ???" \
  491. % (raw_extshtx, ref_first_sheetx, ref_last_sheetx), file=bk.logfile)
  492. print("--- first/last sheet not in range(%d)" % nsheets, file=bk.logfile)
  493. return (-103, -103) # stuffed up somewhere :-(
  494. xlrd_sheetx1 = bk._all_sheets_map[ref_first_sheetx]
  495. xlrd_sheetx2 = bk._all_sheets_map[ref_last_sheetx]
  496. if not(0 <= xlrd_sheetx1 <= xlrd_sheetx2):
  497. return (-3, -3) # internal reference, but to a macro sheet
  498. return xlrd_sheetx1, xlrd_sheetx2
  499. class FormulaError(Exception):
  500. pass
  501. ##
  502. # Used in evaluating formulas.
  503. # The following table describes the kinds and how their values
  504. # are represented.</p>
  505. #
  506. # <table border="1" cellpadding="7">
  507. # <tr>
  508. # <th>Kind symbol</th>
  509. # <th>Kind number</th>
  510. # <th>Value representation</th>
  511. # </tr>
  512. # <tr>
  513. # <td>oBOOL</td>
  514. # <td align="center">3</td>
  515. # <td>integer: 0 => False; 1 => True</td>
  516. # </tr>
  517. # <tr>
  518. # <td>oERR</td>
  519. # <td align="center">4</td>
  520. # <td>None, or an int error code (same as XL_CELL_ERROR in the Cell class).
  521. # </td>
  522. # </tr>
  523. # <tr>
  524. # <td>oMSNG</td>
  525. # <td align="center">5</td>
  526. # <td>Used by Excel as a placeholder for a missing (not supplied) function
  527. # argument. Should *not* appear as a final formula result. Value is None.</td>
  528. # </tr>
  529. # <tr>
  530. # <td>oNUM</td>
  531. # <td align="center">2</td>
  532. # <td>A float. Note that there is no way of distinguishing dates.</td>
  533. # </tr>
  534. # <tr>
  535. # <td>oREF</td>
  536. # <td align="center">-1</td>
  537. # <td>The value is either None or a non-empty list of
  538. # absolute Ref3D instances.<br>
  539. # </td>
  540. # </tr>
  541. # <tr>
  542. # <td>oREL</td>
  543. # <td align="center">-2</td>
  544. # <td>The value is None or a non-empty list of
  545. # fully or partially relative Ref3D instances.
  546. # </td>
  547. # </tr>
  548. # <tr>
  549. # <td>oSTRG</td>
  550. # <td align="center">1</td>
  551. # <td>A Unicode string.</td>
  552. # </tr>
  553. # <tr>
  554. # <td>oUNK</td>
  555. # <td align="center">0</td>
  556. # <td>The kind is unknown or ambiguous. The value is None</td>
  557. # </tr>
  558. # </table>
  559. #<p></p>
  560. class Operand(object):
  561. ##
  562. # None means that the actual value of the operand is a variable
  563. # (depends on cell data), not a constant.
  564. value = None
  565. ##
  566. # oUNK means that the kind of operand is not known unambiguously.
  567. kind = oUNK
  568. ##
  569. # The reconstituted text of the original formula. Function names will be
  570. # in English irrespective of the original language, which doesn't seem
  571. # to be recorded anywhere. The separator is ",", not ";" or whatever else
  572. # might be more appropriate for the end-user's locale; patches welcome.
  573. text = '?'
  574. def __init__(self, akind=None, avalue=None, arank=0, atext='?'):
  575. if akind is not None:
  576. self.kind = akind
  577. if avalue is not None:
  578. self.value = avalue
  579. self.rank = arank
  580. # rank is an internal gizmo (operator precedence);
  581. # it's used in reconstructing formula text.
  582. self.text = atext
  583. def __repr__(self):
  584. kind_text = okind_dict.get(self.kind, "?Unknown kind?")
  585. return "Operand(kind=%s, value=%r, text=%r)" \
  586. % (kind_text, self.value, self.text)
  587. ##
  588. # <p>Represents an absolute or relative 3-dimensional reference to a box
  589. # of one or more cells.<br />
  590. # -- New in version 0.6.0
  591. # </p>
  592. #
  593. # <p>The <i>coords</i> attribute is a tuple of the form:<br />
  594. # (shtxlo, shtxhi, rowxlo, rowxhi, colxlo, colxhi)<br />
  595. # where 0 <= thingxlo <= thingx < thingxhi.<br />
  596. # Note that it is quite possible to have thingx > nthings; for example
  597. # Print_Titles could have colxhi == 256 and/or rowxhi == 65536
  598. # irrespective of how many columns/rows are actually used in the worksheet.
  599. # The caller will need to decide how to handle this situation.
  600. # Keyword: IndexError :-)
  601. # </p>
  602. #
  603. # <p>The components of the coords attribute are also available as individual
  604. # attributes: shtxlo, shtxhi, rowxlo, rowxhi, colxlo, and colxhi.</p>
  605. #
  606. # <p>The <i>relflags</i> attribute is a 6-tuple of flags which indicate whether
  607. # the corresponding (sheet|row|col)(lo|hi) is relative (1) or absolute (0).<br>
  608. # Note that there is necessarily no information available as to what cell(s)
  609. # the reference could possibly be relative to. The caller must decide what if
  610. # any use to make of oREL operands. Note also that a partially relative
  611. # reference may well be a typo.
  612. # For example, define name A1Z10 as $a$1:$z10 (missing $ after z)
  613. # while the cursor is on cell Sheet3!A27.<br>
  614. # The resulting Ref3D instance will have coords = (2, 3, 0, -16, 0, 26)
  615. # and relflags = (0, 0, 0, 1, 0, 0).<br>
  616. # So far, only one possibility of a sheet-relative component in
  617. # a reference has been noticed: a 2D reference located in the "current sheet".
  618. # <br /> This will appear as coords = (0, 1, ...) and relflags = (1, 1, ...).
  619. class Ref3D(tuple):
  620. def __init__(self, atuple):
  621. self.coords = atuple[0:6]
  622. self.relflags = atuple[6:12]
  623. if not self.relflags:
  624. self.relflags = (0, 0, 0, 0, 0, 0)
  625. (self.shtxlo, self.shtxhi,
  626. self.rowxlo, self.rowxhi,
  627. self.colxlo, self.colxhi) = self.coords
  628. def __repr__(self):
  629. if not self.relflags or self.relflags == (0, 0, 0, 0, 0, 0):
  630. return "Ref3D(coords=%r)" % (self.coords, )
  631. else:
  632. return "Ref3D(coords=%r, relflags=%r)" \
  633. % (self.coords, self.relflags)
  634. tAdd = 0x03
  635. tSub = 0x04
  636. tMul = 0x05
  637. tDiv = 0x06
  638. tPower = 0x07
  639. tConcat = 0x08
  640. tLT, tLE, tEQ, tGE, tGT, tNE = range(0x09, 0x0F)
  641. import operator as opr
  642. def nop(x):
  643. return x
  644. def _opr_pow(x, y): return x ** y
  645. def _opr_lt(x, y): return x < y
  646. def _opr_le(x, y): return x <= y
  647. def _opr_eq(x, y): return x == y
  648. def _opr_ge(x, y): return x >= y
  649. def _opr_gt(x, y): return x > y
  650. def _opr_ne(x, y): return x != y
  651. def num2strg(num):
  652. """Attempt to emulate Excel's default conversion
  653. from number to string.
  654. """
  655. s = str(num)
  656. if s.endswith(".0"):
  657. s = s[:-2]
  658. return s
  659. _arith_argdict = {oNUM: nop, oSTRG: float}
  660. _cmp_argdict = {oNUM: nop, oSTRG: nop}
  661. # Seems no conversions done on relops; in Excel, "1" > 9 produces TRUE.
  662. _strg_argdict = {oNUM:num2strg, oSTRG:nop}
  663. binop_rules = {
  664. tAdd: (_arith_argdict, oNUM, opr.add, 30, '+'),
  665. tSub: (_arith_argdict, oNUM, opr.sub, 30, '-'),
  666. tMul: (_arith_argdict, oNUM, opr.mul, 40, '*'),
  667. tDiv: (_arith_argdict, oNUM, opr.truediv, 40, '/'),
  668. tPower: (_arith_argdict, oNUM, _opr_pow, 50, '^',),
  669. tConcat:(_strg_argdict, oSTRG, opr.add, 20, '&'),
  670. tLT: (_cmp_argdict, oBOOL, _opr_lt, 10, '<'),
  671. tLE: (_cmp_argdict, oBOOL, _opr_le, 10, '<='),
  672. tEQ: (_cmp_argdict, oBOOL, _opr_eq, 10, '='),
  673. tGE: (_cmp_argdict, oBOOL, _opr_ge, 10, '>='),
  674. tGT: (_cmp_argdict, oBOOL, _opr_gt, 10, '>'),
  675. tNE: (_cmp_argdict, oBOOL, _opr_ne, 10, '<>'),
  676. }
  677. unop_rules = {
  678. 0x13: (lambda x: -x, 70, '-', ''), # unary minus
  679. 0x12: (lambda x: x, 70, '+', ''), # unary plus
  680. 0x14: (lambda x: x / 100.0, 60, '', '%'),# percent
  681. }
  682. LEAF_RANK = 90
  683. FUNC_RANK = 90
  684. STACK_ALARM_LEVEL = 5
  685. STACK_PANIC_LEVEL = 10
  686. def evaluate_name_formula(bk, nobj, namex, blah=0, level=0):
  687. if level > STACK_ALARM_LEVEL:
  688. blah = 1
  689. data = nobj.raw_formula
  690. fmlalen = nobj.basic_formula_len
  691. bv = bk.biff_version
  692. reldelta = 1 # All defined name formulas use "Method B" [OOo docs]
  693. if blah:
  694. print("::: evaluate_name_formula %r %r %d %d %r level=%d" \
  695. % (namex, nobj.name, fmlalen, bv, data, level), file=bk.logfile)
  696. hex_char_dump(data, 0, fmlalen, fout=bk.logfile)
  697. if level > STACK_PANIC_LEVEL:
  698. raise XLRDError("Excessive indirect references in NAME formula")
  699. sztab = szdict[bv]
  700. pos = 0
  701. stack = []
  702. any_rel = 0
  703. any_err = 0
  704. any_external = 0
  705. unk_opnd = Operand(oUNK, None)
  706. error_opnd = Operand(oERR, None)
  707. spush = stack.append
  708. def do_binop(opcd, stk):
  709. assert len(stk) >= 2
  710. bop = stk.pop()
  711. aop = stk.pop()
  712. argdict, result_kind, func, rank, sym = binop_rules[opcd]
  713. otext = ''.join([
  714. '('[:aop.rank < rank],
  715. aop.text,
  716. ')'[:aop.rank < rank],
  717. sym,
  718. '('[:bop.rank < rank],
  719. bop.text,
  720. ')'[:bop.rank < rank],
  721. ])
  722. resop = Operand(result_kind, None, rank, otext)
  723. try:
  724. bconv = argdict[bop.kind]
  725. aconv = argdict[aop.kind]
  726. except KeyError:
  727. stk.append(resop)
  728. return
  729. if bop.value is None or aop.value is None:
  730. stk.append(resop)
  731. return
  732. bval = bconv(bop.value)
  733. aval = aconv(aop.value)
  734. result = func(aval, bval)
  735. if result_kind == oBOOL:
  736. result = 1 if result else 0
  737. resop.value = result
  738. stk.append(resop)
  739. def do_unaryop(opcode, result_kind, stk):
  740. assert len(stk) >= 1
  741. aop = stk.pop()
  742. val = aop.value
  743. func, rank, sym1, sym2 = unop_rules[opcode]
  744. otext = ''.join([
  745. sym1,
  746. '('[:aop.rank < rank],
  747. aop.text,
  748. ')'[:aop.rank < rank],
  749. sym2,
  750. ])
  751. if val is not None:
  752. val = func(val)
  753. stk.append(Operand(result_kind, val, rank, otext))
  754. def not_in_name_formula(op_arg, oname_arg):
  755. msg = "ERROR *** Token 0x%02x (%s) found in NAME formula" \
  756. % (op_arg, oname_arg)
  757. raise FormulaError(msg)
  758. if fmlalen == 0:
  759. stack = [unk_opnd]
  760. while 0 <= pos < fmlalen:
  761. op = BYTES_ORD(data[pos])
  762. opcode = op & 0x1f
  763. optype = (op & 0x60) >> 5
  764. if optype:
  765. opx = opcode + 32
  766. else:
  767. opx = opcode
  768. oname = onames[opx] # + [" RVA"][optype]
  769. sz = sztab[opx]
  770. if blah:
  771. print("Pos:%d Op:0x%02x Name:t%s Sz:%d opcode:%02xh optype:%02xh" \
  772. % (pos, op, oname, sz, opcode, optype), file=bk.logfile)
  773. print("Stack =", stack, file=bk.logfile)
  774. if sz == -2:
  775. msg = 'ERROR *** Unexpected token 0x%02x ("%s"); biff_version=%d' \
  776. % (op, oname, bv)
  777. raise FormulaError(msg)
  778. if not optype:
  779. if 0x00 <= opcode <= 0x02: # unk_opnd, tExp, tTbl
  780. not_in_name_formula(op, oname)
  781. elif 0x03 <= opcode <= 0x0E:
  782. # Add, Sub, Mul, Div, Power
  783. # tConcat
  784. # tLT, ..., tNE
  785. do_binop(opcode, stack)
  786. elif opcode == 0x0F: # tIsect
  787. if blah: print("tIsect pre", stack, file=bk.logfile)
  788. assert len(stack) >= 2
  789. bop = stack.pop()
  790. aop = stack.pop()
  791. sym = ' '
  792. rank = 80 ########## check #######
  793. otext = ''.join([
  794. '('[:aop.rank < rank],
  795. aop.text,
  796. ')'[:aop.rank < rank],
  797. sym,
  798. '('[:bop.rank < rank],
  799. bop.text,
  800. ')'[:bop.rank < rank],
  801. ])
  802. res = Operand(oREF)
  803. res.text = otext
  804. if bop.kind == oERR or aop.kind == oERR:
  805. res.kind = oERR
  806. elif bop.kind == oUNK or aop.kind == oUNK:
  807. # This can happen with undefined
  808. # (go search in the current sheet) labels.
  809. # For example =Bob Sales
  810. # Each label gets a NAME record with an empty formula (!)
  811. # Evaluation of the tName token classifies it as oUNK
  812. # res.kind = oREF
  813. pass
  814. elif bop.kind == oREF == aop.kind:
  815. if aop.value is not None and bop.value is not None:
  816. assert len(aop.value) == 1
  817. assert len(bop.value) == 1
  818. coords = do_box_funcs(
  819. tIsectFuncs, aop.value[0], bop.value[0])
  820. res.value = [Ref3D(coords)]
  821. elif bop.kind == oREL == aop.kind:
  822. res.kind = oREL
  823. if aop.value is not None and bop.value is not None:
  824. assert len(aop.value) == 1
  825. assert len(bop.value) == 1
  826. coords = do_box_funcs(
  827. tIsectFuncs, aop.value[0], bop.value[0])
  828. relfa = aop.value[0].relflags
  829. relfb = bop.value[0].relflags
  830. if relfa == relfb:
  831. res.value = [Ref3D(coords + relfa)]
  832. else:
  833. pass
  834. spush(res)
  835. if blah: print("tIsect post", stack, file=bk.logfile)
  836. elif opcode == 0x10: # tList
  837. if blah: print("tList pre", stack, file=bk.logfile)
  838. assert len(stack) >= 2
  839. bop = stack.pop()
  840. aop = stack.pop()
  841. sym = ','
  842. rank = 80 ########## check #######
  843. otext = ''.join([
  844. '('[:aop.rank < rank],
  845. aop.text,
  846. ')'[:aop.rank < rank],
  847. sym,
  848. '('[:bop.rank < rank],
  849. bop.text,
  850. ')'[:bop.rank < rank],
  851. ])
  852. res = Operand(oREF, None, rank, otext)
  853. if bop.kind == oERR or aop.kind == oERR:
  854. res.kind = oERR
  855. elif bop.kind in (oREF, oREL) and aop.kind in (oREF, oREL):
  856. res.kind = oREF
  857. if aop.kind == oREL or bop.kind == oREL:
  858. res.kind = oREL
  859. if aop.value is not None and bop.value is not None:
  860. assert len(aop.value) >= 1
  861. assert len(bop.value) == 1
  862. res.value = aop.value + bop.value
  863. else:
  864. pass
  865. spush(res)
  866. if blah: print("tList post", stack, file=bk.logfile)
  867. elif opcode == 0x11: # tRange
  868. if blah: print("tRange pre", stack, file=bk.logfile)
  869. assert len(stack) >= 2
  870. bop = stack.pop()
  871. aop = stack.pop()
  872. sym = ':'
  873. rank = 80 ########## check #######
  874. otext = ''.join([
  875. '('[:aop.rank < rank],
  876. aop.text,
  877. ')'[:aop.rank < rank],
  878. sym,
  879. '('[:bop.rank < rank],
  880. bop.text,
  881. ')'[:bop.rank < rank],
  882. ])
  883. res = Operand(oREF, None, rank, otext)
  884. if bop.kind == oERR or aop.kind == oERR:
  885. res = oERR
  886. elif bop.kind == oREF == aop.kind:
  887. if aop.value is not None and bop.value is not None:
  888. assert len(aop.value) == 1
  889. assert len(bop.value) == 1
  890. coords = do_box_funcs(
  891. tRangeFuncs, aop.value[0], bop.value[0])
  892. res.value = [Ref3D(coords)]
  893. elif bop.kind == oREL == aop.kind:
  894. res.kind = oREL
  895. if aop.value is not None and bop.value is not None:
  896. assert len(aop.value) == 1
  897. assert len(bop.value) == 1
  898. coords = do_box_funcs(
  899. tRangeFuncs, aop.value[0], bop.value[0])
  900. relfa = aop.value[0].relflags
  901. relfb = bop.value[0].relflags
  902. if relfa == relfb:
  903. res.value = [Ref3D(coords + relfa)]
  904. else:
  905. pass
  906. spush(res)
  907. if blah: print("tRange post", stack, file=bk.logfile)
  908. elif 0x12 <= opcode <= 0x14: # tUplus, tUminus, tPercent
  909. do_unaryop(opcode, oNUM, stack)
  910. elif opcode == 0x15: # tParen
  911. # source cosmetics
  912. pass
  913. elif opcode == 0x16: # tMissArg
  914. spush(Operand(oMSNG, None, LEAF_RANK, ''))
  915. elif opcode == 0x17: # tStr
  916. if bv <= 70:
  917. strg, newpos = unpack_string_update_pos(
  918. data, pos+1, bk.encoding, lenlen=1)
  919. else:
  920. strg, newpos = unpack_unicode_update_pos(
  921. data, pos+1, lenlen=1)
  922. sz = newpos - pos
  923. if blah: print(" sz=%d strg=%r" % (sz, strg), file=bk.logfile)
  924. text = '"' + strg.replace('"', '""') + '"'
  925. spush(Operand(oSTRG, strg, LEAF_RANK, text))
  926. elif opcode == 0x18: # tExtended
  927. # new with BIFF 8
  928. assert bv >= 80
  929. # not in OOo docs
  930. raise FormulaError("tExtended token not implemented")
  931. elif opcode == 0x19: # tAttr
  932. subop, nc = unpack("<BH", data[pos+1:pos+4])
  933. subname = tAttrNames.get(subop, "??Unknown??")
  934. if subop == 0x04: # Choose
  935. sz = nc * 2 + 6
  936. elif subop == 0x10: # Sum (single arg)
  937. sz = 4
  938. if blah: print("tAttrSum", stack, file=bk.logfile)
  939. assert len(stack) >= 1
  940. aop = stack[-1]
  941. otext = 'SUM(%s)' % aop.text
  942. stack[-1] = Operand(oNUM, None, FUNC_RANK, otext)
  943. else:
  944. sz = 4
  945. if blah:
  946. print(" subop=%02xh subname=t%s sz=%d nc=%02xh" \
  947. % (subop, subname, sz, nc), file=bk.logfile)
  948. elif 0x1A <= opcode <= 0x1B: # tSheet, tEndSheet
  949. assert bv < 50
  950. raise FormulaError("tSheet & tEndsheet tokens not implemented")
  951. elif 0x1C <= opcode <= 0x1F: # tErr, tBool, tInt, tNum
  952. inx = opcode - 0x1C
  953. nb = [1, 1, 2, 8][inx]
  954. kind = [oERR, oBOOL, oNUM, oNUM][inx]
  955. value, = unpack("<" + "BBHd"[inx], data[pos+1:pos+1+nb])
  956. if inx == 2: # tInt
  957. value = float(value)
  958. text = str(value)
  959. elif inx == 3: # tNum
  960. text = str(value)
  961. elif inx == 1: # tBool
  962. text = ('FALSE', 'TRUE')[value]
  963. else:
  964. text = '"' +error_text_from_code[value] + '"'
  965. spush(Operand(kind, value, LEAF_RANK, text))
  966. else:
  967. raise FormulaError("Unhandled opcode: 0x%02x" % opcode)
  968. if sz <= 0:
  969. raise FormulaError("Size not set for opcode 0x%02x" % opcode)
  970. pos += sz
  971. continue
  972. if opcode == 0x00: # tArray
  973. spush(unk_opnd)
  974. elif opcode == 0x01: # tFunc
  975. nb = 1 + int(bv >= 40)
  976. funcx = unpack("<" + " BH"[nb], data[pos+1:pos+1+nb])[0]
  977. func_attrs = func_defs.get(funcx, None)
  978. if not func_attrs:
  979. print("*** formula/tFunc unknown FuncID:%d" \
  980. % funcx, file=bk.logfile)
  981. spush(unk_opnd)
  982. else:
  983. func_name, nargs = func_attrs[:2]
  984. if blah:
  985. print(" FuncID=%d name=%s nargs=%d" \
  986. % (funcx, func_name, nargs), file=bk.logfile)
  987. assert len(stack) >= nargs
  988. if nargs:
  989. argtext = listsep.join([arg.text for arg in stack[-nargs:]])
  990. otext = "%s(%s)" % (func_name, argtext)
  991. del stack[-nargs:]
  992. else:
  993. otext = func_name + "()"
  994. res = Operand(oUNK, None, FUNC_RANK, otext)
  995. spush(res)
  996. elif opcode == 0x02: #tFuncVar
  997. nb = 1 + int(bv >= 40)
  998. nargs, funcx = unpack("<B" + " BH"[nb], data[pos+1:pos+2+nb])
  999. prompt, nargs = divmod(nargs, 128)
  1000. macro, funcx = divmod(funcx, 32768)
  1001. if blah:
  1002. print(" FuncID=%d nargs=%d macro=%d prompt=%d" \
  1003. % (funcx, nargs, macro, prompt), file=bk.logfile)
  1004. func_attrs = func_defs.get(funcx, None)
  1005. if not func_attrs:
  1006. print("*** formula/tFuncVar unknown FuncID:%d" \
  1007. % funcx, file=bk.logfile)
  1008. spush(unk_opnd)
  1009. else:
  1010. func_name, minargs, maxargs = func_attrs[:3]
  1011. if blah:
  1012. print(" name: %r, min~max args: %d~%d" \
  1013. % (func_name, minargs, maxargs), file=bk.logfile)
  1014. assert minargs <= nargs <= maxargs
  1015. assert len(stack) >= nargs
  1016. assert len(stack) >= nargs
  1017. argtext = listsep.join([arg.text for arg in stack[-nargs:]])
  1018. otext = "%s(%s)" % (func_name, argtext)
  1019. res = Operand(oUNK, None, FUNC_RANK, otext)
  1020. if funcx == 1: # IF
  1021. testarg = stack[-nargs]
  1022. if testarg.kind not in (oNUM, oBOOL):
  1023. if blah and testarg.kind != oUNK:
  1024. print("IF testarg kind?", file=bk.logfile)
  1025. elif testarg.value not in (0, 1):
  1026. if blah and testarg.value is not None:
  1027. print("IF testarg value?", file=bk.logfile)
  1028. else:
  1029. if nargs == 2 and not testarg.value:
  1030. # IF(FALSE, tv) => FALSE
  1031. res.kind, res.value = oBOOL, 0
  1032. else:
  1033. respos = -nargs + 2 - int(testarg.value)
  1034. chosen = stack[respos]
  1035. if chosen.kind == oMSNG:
  1036. res.kind, res.value = oNUM, 0
  1037. else:
  1038. res.kind, res.value = chosen.kind, chosen.value
  1039. if blah:
  1040. print("$$$$$$ IF => constant", file=bk.logfile)
  1041. elif funcx == 100: # CHOOSE
  1042. testarg = stack[-nargs]
  1043. if testarg.kind == oNUM:
  1044. if 1 <= testarg.value < nargs:
  1045. chosen = stack[-nargs + int(testarg.value)]
  1046. if chosen.kind == oMSNG:
  1047. res.kind, res.value = oNUM, 0
  1048. else:
  1049. res.kind, res.value = chosen.kind, chosen.value
  1050. del stack[-nargs:]
  1051. spush(res)
  1052. elif opcode == 0x03: #tName
  1053. tgtnamex = unpack("<H", data[pos+1:pos+3])[0] - 1
  1054. # Only change with BIFF version is number of trailing UNUSED bytes!
  1055. if blah: print(" tgtnamex=%d" % tgtnamex, file=bk.logfile)
  1056. tgtobj = bk.name_obj_list[tgtnamex]
  1057. if not tgtobj.evaluated:
  1058. ### recursive ###
  1059. evaluate_name_formula(bk, tgtobj, tgtnamex, blah, level+1)
  1060. if tgtobj.macro or tgtobj.binary \
  1061. or tgtobj.any_err:
  1062. if blah:
  1063. tgtobj.dump(
  1064. bk.logfile,
  1065. header="!!! tgtobj has problems!!!",
  1066. footer="----------- --------",
  1067. )
  1068. res = Operand(oUNK, None)
  1069. any_err = any_err or tgtobj.macro or tgtobj.binary or tgtobj.any_err
  1070. any_rel = any_rel or tgtobj.any_rel
  1071. else:
  1072. assert len(tgtobj.stack) == 1
  1073. res = copy.deepcopy(tgtobj.stack[0])
  1074. res.rank = LEAF_RANK
  1075. if tgtobj.scope == -1:
  1076. res.text = tgtobj.name
  1077. else:
  1078. res.text = "%s!%s" \
  1079. % (bk._sheet_names[tgtobj.scope], tgtobj.name)
  1080. if blah:
  1081. print(" tName: setting text to", repr(res.text), file=bk.logfile)
  1082. spush(res)
  1083. elif opcode == 0x04: # tRef
  1084. # not_in_name_formula(op, oname)
  1085. res = get_cell_addr(data, pos+1, bv, reldelta)
  1086. if blah: print(" ", res, file=bk.logfile)
  1087. rowx, colx, row_rel, col_rel = res
  1088. shx1 = shx2 = 0 ####### N.B. relative to the CURRENT SHEET
  1089. any_rel = 1
  1090. coords = (shx1, shx2+1, rowx, rowx+1, colx, colx+1)
  1091. if blah: print(" ", coords, file=bk.logfile)
  1092. res = Operand(oUNK, None)
  1093. if optype == 1:
  1094. relflags = (1, 1, row_rel, row_rel, col_rel, col_rel)
  1095. res = Operand(oREL, [Ref3D(coords + relflags)])
  1096. spush(res)
  1097. elif opcode == 0x05: # tArea
  1098. # not_in_name_formula(op, oname)
  1099. res1, res2 = get_cell_range_addr(data, pos+1, bv, reldelta)
  1100. if blah: print(" ", res1, res2, file=bk.logfile)
  1101. rowx1, colx1, row_rel1, col_rel1 = res1
  1102. rowx2, colx2, row_rel2, col_rel2 = res2
  1103. shx1 = shx2 = 0 ####### N.B. relative to the CURRENT SHEET
  1104. any_rel = 1
  1105. coords = (shx1, shx2+1, rowx1, rowx2+1, colx1, colx2+1)
  1106. if blah: print(" ", coords, file=bk.logfile)
  1107. res = Operand(oUNK, None)
  1108. if optype == 1:
  1109. relflags = (1, 1, row_rel1, row_rel2, col_rel1, col_rel2)
  1110. res = Operand(oREL, [Ref3D(coords + relflags)])
  1111. spush(res)
  1112. elif opcode == 0x06: # tMemArea
  1113. not_in_name_formula(op, oname)
  1114. elif opcode == 0x09: # tMemFunc
  1115. nb = unpack("<H", data[pos+1:pos+3])[0]
  1116. if blah: print(" %d bytes of cell ref formula" % nb, file=bk.logfile)
  1117. # no effect on stack
  1118. elif opcode == 0x0C: #tRefN
  1119. not_in_name_formula(op, oname)
  1120. # res = get_cell_addr(data, pos+1, bv, reldelta=1)
  1121. # # note *ALL* tRefN usage has signed offset for relative addresses
  1122. # any_rel = 1
  1123. # if blah: print >> bk.logfile, " ", res
  1124. # spush(res)
  1125. elif opcode == 0x0D: #tAreaN
  1126. not_in_name_formula(op, oname)
  1127. # res = get_cell_range_addr(data, pos+1, bv, reldelta=1)
  1128. # # note *ALL* tAreaN usage has signed offset for relative addresses
  1129. # any_rel = 1
  1130. # if blah: print >> bk.logfile, " ", res
  1131. elif opcode == 0x1A: # tRef3d
  1132. if bv >= 80:
  1133. res = get_cell_addr(data, pos+3, bv, reldelta)
  1134. refx = unpack("<H", data[pos+1:pos+3])[0]
  1135. shx1, shx2 = get_externsheet_local_range(bk, refx, blah)
  1136. else:
  1137. res = get_cell_addr(data, pos+15, bv, reldelta)
  1138. raw_extshtx, raw_shx1, raw_shx2 = \
  1139. unpack("<hxxxxxxxxhh", data[pos+1:pos+15])
  1140. if blah:
  1141. print("tRef3d", raw_extshtx, raw_shx1, raw_shx2, file=bk.logfile)
  1142. shx1, shx2 = get_externsheet_local_range_b57(
  1143. bk, raw_extshtx, raw_shx1, raw_shx2, blah)
  1144. rowx, colx, row_rel, col_rel = res
  1145. is_rel = row_rel or col_rel
  1146. any_rel = any_rel or is_rel
  1147. coords = (shx1, shx2+1, rowx, rowx+1, colx, colx+1)
  1148. any_err |= shx1 < -1
  1149. if blah: print(" ", coords, file=bk.logfile)
  1150. res = Operand(oUNK, None)
  1151. if is_rel:
  1152. relflags = (0, 0, row_rel, row_rel, col_rel, col_rel)
  1153. ref3d = Ref3D(coords + relflags)
  1154. res.kind = oREL
  1155. res.text = rangename3drel(bk, ref3d, r1c1=1)
  1156. else:
  1157. ref3d = Ref3D(coords)
  1158. res.kind = oREF
  1159. res.text = rangename3d(bk, ref3d)
  1160. res.rank = LEAF_RANK
  1161. if optype == 1:
  1162. res.value = [ref3d]
  1163. spush(res)
  1164. elif opcode == 0x1B: # tArea3d
  1165. if bv >= 80:
  1166. res1, res2 = get_cell_range_addr(data, pos+3, bv, reldelta)
  1167. refx = unpack("<H", data[pos+1:pos+3])[0]
  1168. shx1, shx2 = get_externsheet_local_range(bk, refx, blah)
  1169. else:
  1170. res1, res2 = get_cell_range_addr(data, pos+15, bv, reldelta)
  1171. raw_extshtx, raw_shx1, raw_shx2 = \
  1172. unpack("<hxxxxxxxxhh", data[pos+1:pos+15])
  1173. if blah:
  1174. print("tArea3d", raw_extshtx, raw_shx1, raw_shx2, file=bk.logfile)
  1175. shx1, shx2 = get_externsheet_local_range_b57(
  1176. bk, raw_extshtx, raw_shx1, raw_shx2, blah)
  1177. any_err |= shx1 < -1
  1178. rowx1, colx1, row_rel1, col_rel1 = res1
  1179. rowx2, colx2, row_rel2, col_rel2 = res2
  1180. is_rel = row_rel1 or col_rel1 or row_rel2 or col_rel2
  1181. any_rel = any_rel or is_rel
  1182. coords = (shx1, shx2+1, rowx1, rowx2+1, colx1, colx2+1)
  1183. if blah: print(" ", coords, file=bk.logfile)
  1184. res = Operand(oUNK, None)
  1185. if is_rel:
  1186. relflags = (0, 0, row_rel1, row_rel2, col_rel1, col_rel2)
  1187. ref3d = Ref3D(coords + relflags)
  1188. res.kind = oREL
  1189. res.text = rangename3drel(bk, ref3d, r1c1=1)
  1190. else:
  1191. ref3d = Ref3D(coords)
  1192. res.kind = oREF
  1193. res.text = rangename3d(bk, ref3d)
  1194. res.rank = LEAF_RANK
  1195. if optype == 1:
  1196. res.value = [ref3d]
  1197. spush(res)
  1198. elif opcode == 0x19: # tNameX
  1199. dodgy = 0
  1200. res = Operand(oUNK, None)
  1201. if bv >= 80:
  1202. refx, tgtnamex = unpack("<HH", data[pos+1:pos+5])
  1203. tgtnamex -= 1
  1204. origrefx = refx
  1205. else:
  1206. refx, tgtnamex = unpack("<hxxxxxxxxH", data[pos+1:pos+13])
  1207. tgtnamex -= 1
  1208. origrefx = refx
  1209. if refx > 0:
  1210. refx -= 1
  1211. elif refx < 0:
  1212. refx = -refx - 1
  1213. else:
  1214. dodgy = 1
  1215. if blah:
  1216. print(" origrefx=%d refx=%d tgtnamex=%d dodgy=%d" \
  1217. % (origrefx, refx, tgtnamex, dodgy), file=bk.logfile)
  1218. if tgtnamex == namex:
  1219. if blah: print("!!!! Self-referential !!!!", file=bk.logfile)
  1220. dodgy = any_err = 1
  1221. if not dodgy:
  1222. if bv >= 80:
  1223. shx1, shx2 = get_externsheet_local_range(bk, refx, blah)
  1224. elif origrefx > 0:
  1225. shx1, shx2 = (-4, -4) # external ref
  1226. else:
  1227. exty = bk._externsheet_type_b57[refx]
  1228. if exty == 4: # non-specific sheet in own doc't
  1229. shx1, shx2 = (-1, -1) # internal, any sheet
  1230. else:
  1231. shx1, shx2 = (-666, -666)
  1232. if dodgy or shx1 < -1:
  1233. otext = "<<Name #%d in external(?) file #%d>>" \
  1234. % (tgtnamex, origrefx)
  1235. res = Operand(oUNK, None, LEAF_RANK, otext)
  1236. else:
  1237. tgtobj = bk.name_obj_list[tgtnamex]
  1238. if not tgtobj.evaluated:
  1239. ### recursive ###
  1240. evaluate_name_formula(bk, tgtobj, tgtnamex, blah, level+1)
  1241. if tgtobj.macro or tgtobj.binary \
  1242. or tgtobj.any_err:
  1243. if blah:
  1244. tgtobj.dump(
  1245. bk.logfile,
  1246. header="!!! bad tgtobj !!!",
  1247. footer="------------------",
  1248. )
  1249. res = Operand(oUNK, None)
  1250. any_err = any_err or tgtobj.macro or tgtobj.binary or tgtobj.any_err
  1251. any_rel = any_rel or tgtobj.any_rel
  1252. else:
  1253. assert len(tgtobj.stack) == 1
  1254. res = copy.deepcopy(tgtobj.stack[0])
  1255. res.rank = LEAF_RANK
  1256. if tgtobj.scope == -1:
  1257. res.text = tgtobj.name
  1258. else:
  1259. res.text = "%s!%s" \
  1260. % (bk._sheet_names[tgtobj.scope], tgtobj.name)
  1261. if blah:
  1262. print(" tNameX: setting text to", repr(res.text), file=bk.logfile)
  1263. spush(res)
  1264. elif opcode in error_opcodes:
  1265. any_err = 1
  1266. spush(error_opnd)
  1267. else:
  1268. if blah:
  1269. print("FORMULA: /// Not handled yet: t" + oname, file=bk.logfile)
  1270. any_err = 1
  1271. if sz <= 0:
  1272. raise FormulaError("Fatal: token size is not positive")
  1273. pos += sz
  1274. any_rel = not not any_rel
  1275. if blah:
  1276. fprintf(bk.logfile, "End of formula. level=%d any_rel=%d any_err=%d stack=%r\n",
  1277. level, not not any_rel, any_err, stack)
  1278. if len(stack) >= 2:
  1279. print("*** Stack has unprocessed args", file=bk.logfile)
  1280. print(file=bk.logfile)
  1281. nobj.stack = stack
  1282. if len(stack) != 1:
  1283. nobj.result = None
  1284. else:
  1285. nobj.result = stack[0]
  1286. nobj.any_rel = any_rel
  1287. nobj.any_err = any_err
  1288. nobj.any_external = any_external
  1289. nobj.evaluated = 1
  1290. #### under construction #############################################################################
  1291. def decompile_formula(bk, fmla, fmlalen,
  1292. fmlatype=None, browx=None, bcolx=None,
  1293. blah=0, level=0, r1c1=0):
  1294. if level > STACK_ALARM_LEVEL:
  1295. blah = 1
  1296. reldelta = fmlatype in (FMLA_TYPE_SHARED, FMLA_TYPE_NAME, FMLA_TYPE_COND_FMT, FMLA_TYPE_DATA_VAL)
  1297. data = fmla
  1298. bv = bk.biff_version
  1299. if blah:
  1300. print("::: decompile_formula len=%d fmlatype=%r browx=%r bcolx=%r reldelta=%d %r level=%d" \
  1301. % (fmlalen, fmlatype, browx, bcolx, reldelta, data, level), file=bk.logfile)
  1302. hex_char_dump(data, 0, fmlalen, fout=bk.logfile)
  1303. if level > STACK_PANIC_LEVEL:
  1304. raise XLRDError("Excessive indirect references in formula")
  1305. sztab = szdict[bv]
  1306. pos = 0
  1307. stack = []
  1308. any_rel = 0
  1309. any_err = 0
  1310. any_external = 0
  1311. unk_opnd = Operand(oUNK, None)
  1312. error_opnd = Operand(oERR, None)
  1313. spush = stack.append
  1314. def do_binop(opcd, stk):
  1315. assert len(stk) >= 2
  1316. bop = stk.pop()
  1317. aop = stk.pop()
  1318. argdict, result_kind, func, rank, sym = binop_rules[opcd]
  1319. otext = ''.join([
  1320. '('[:aop.rank < rank],
  1321. aop.text,
  1322. ')'[:aop.rank < rank],
  1323. sym,
  1324. '('[:bop.rank < rank],
  1325. bop.text,
  1326. ')'[:bop.rank < rank],
  1327. ])
  1328. resop = Operand(result_kind, None, rank, otext)
  1329. stk.append(resop)
  1330. def do_unaryop(opcode, result_kind, stk):
  1331. assert len(stk) >= 1
  1332. aop = stk.pop()
  1333. func, rank, sym1, sym2 = unop_rules[opcode]
  1334. otext = ''.join([
  1335. sym1,
  1336. '('[:aop.rank < rank],
  1337. aop.text,
  1338. ')'[:aop.rank < rank],
  1339. sym2,
  1340. ])
  1341. stk.append(Operand(result_kind, None, rank, otext))
  1342. def unexpected_opcode(op_arg, oname_arg):
  1343. msg = "ERROR *** Unexpected token 0x%02x (%s) found in formula type %s" \
  1344. % (op_arg, oname_arg, FMLA_TYPEDESCR_MAP[fmlatype])
  1345. print(msg, file=bk.logfile)
  1346. # raise FormulaError(msg)
  1347. if fmlalen == 0:
  1348. stack = [unk_opnd]
  1349. while 0 <= pos < fmlalen:
  1350. op = BYTES_ORD(data[pos])
  1351. opcode = op & 0x1f
  1352. optype = (op & 0x60) >> 5
  1353. if optype:
  1354. opx = opcode + 32
  1355. else:
  1356. opx = opcode
  1357. oname = onames[opx] # + [" RVA"][optype]
  1358. sz = sztab[opx]
  1359. if blah:
  1360. print("Pos:%d Op:0x%02x opname:t%s Sz:%d opcode:%02xh optype:%02xh" \
  1361. % (pos, op, oname, sz, opcode, optype), file=bk.logfile)
  1362. print("Stack =", stack, file=bk.logfile)
  1363. if sz == -2:
  1364. msg = 'ERROR *** Unexpected token 0x%02x ("%s"); biff_version=%d' \
  1365. % (op, oname, bv)
  1366. raise FormulaError(msg)
  1367. if _TOKEN_NOT_ALLOWED(opx, 0) & fmlatype:
  1368. unexpected_opcode(op, oname)
  1369. if not optype:
  1370. if opcode <= 0x01: # tExp
  1371. if bv >= 30:
  1372. fmt = '<x2H'
  1373. else:
  1374. fmt = '<xHB'
  1375. assert pos == 0 and fmlalen == sz and not stack
  1376. rowx, colx = unpack(fmt, data)
  1377. text = "SHARED FMLA at rowx=%d colx=%d" % (rowx, colx)
  1378. spush(Operand(oUNK, None, LEAF_RANK, text))
  1379. if not fmlatype & (FMLA_TYPE_CELL | FMLA_TYPE_ARRAY):
  1380. unexpected_opcode(op, oname)
  1381. elif 0x03 <= opcode <= 0x0E:
  1382. # Add, Sub, Mul, Div, Power
  1383. # tConcat
  1384. # tLT, ..., tNE
  1385. do_binop(opcode, stack)
  1386. elif opcode == 0x0F: # tIsect
  1387. if blah: print("tIsect pre", stack, file=bk.logfile)
  1388. assert len(stack) >= 2
  1389. bop = stack.pop()
  1390. aop = stack.pop()
  1391. sym = ' '
  1392. rank = 80 ########## check #######
  1393. otext = ''.join([
  1394. '('[:aop.rank < rank],
  1395. aop.text,
  1396. ')'[:aop.rank < rank],
  1397. sym,
  1398. '('[:bop.rank < rank],
  1399. bop.text,
  1400. ')'[:bop.rank < rank],
  1401. ])
  1402. res = Operand(oREF)
  1403. res.text = otext
  1404. if bop.kind == oERR or aop.kind == oERR:
  1405. res.kind = oERR
  1406. elif bop.kind == oUNK or aop.kind == oUNK:
  1407. # This can happen with undefined
  1408. # (go search in the current sheet) labels.
  1409. # For example =Bob Sales
  1410. # Each label gets a NAME record with an empty formula (!)
  1411. # Evaluation of the tName token classifies it as oUNK
  1412. # res.kind = oREF
  1413. pass
  1414. elif bop.kind == oREF == aop.kind:
  1415. pass
  1416. elif bop.kind == oREL == aop.kind:
  1417. res.kind = oREL
  1418. else:
  1419. pass
  1420. spush(res)
  1421. if blah: print("tIsect post", stack, file=bk.logfile)
  1422. elif opcode == 0x10: # tList
  1423. if blah: print("tList pre", stack, file=bk.logfile)
  1424. assert len(stack) >= 2
  1425. bop = stack.pop()
  1426. aop = stack.pop()
  1427. sym = ','
  1428. rank = 80 ########## check #######
  1429. otext = ''.join([
  1430. '('[:aop.rank < rank],
  1431. aop.text,
  1432. ')'[:aop.rank < rank],
  1433. sym,
  1434. '('[:bop.rank < rank],
  1435. bop.text,
  1436. ')'[:bop.rank < rank],
  1437. ])
  1438. res = Operand(oREF, None, rank, otext)
  1439. if bop.kind == oERR or aop.kind == oERR:
  1440. res.kind = oERR
  1441. elif bop.kind in (oREF, oREL) and aop.kind in (oREF, oREL):
  1442. res.kind = oREF
  1443. if aop.kind == oREL or bop.kind == oREL:
  1444. res.kind = oREL
  1445. else:
  1446. pass
  1447. spush(res)
  1448. if blah: print("tList post", stack, file=bk.logfile)
  1449. elif opcode == 0x11: # tRange
  1450. if blah: print("tRange pre", stack, file=bk.logfile)
  1451. assert len(stack) >= 2
  1452. bop = stack.pop()
  1453. aop = stack.pop()
  1454. sym = ':'
  1455. rank = 80 ########## check #######
  1456. otext = ''.join([
  1457. '('[:aop.rank < rank],
  1458. aop.text,
  1459. ')'[:aop.rank < rank],
  1460. sym,
  1461. '('[:bop.rank < rank],
  1462. bop.text,
  1463. ')'[:bop.rank < rank],
  1464. ])
  1465. res = Operand(oREF, None, rank, otext)
  1466. if bop.kind == oERR or aop.kind == oERR:
  1467. res = oERR
  1468. elif bop.kind == oREF == aop.kind:
  1469. pass
  1470. else:
  1471. pass
  1472. spush(res)
  1473. if blah: print("tRange post", stack, file=bk.logfile)
  1474. elif 0x12 <= opcode <= 0x14: # tUplus, tUminus, tPercent
  1475. do_unaryop(opcode, oNUM, stack)
  1476. elif opcode == 0x15: # tParen
  1477. # source cosmetics
  1478. pass
  1479. elif opcode == 0x16: # tMissArg
  1480. spush(Operand(oMSNG, None, LEAF_RANK, ''))
  1481. elif opcode == 0x17: # tStr
  1482. if bv <= 70:
  1483. strg, newpos = unpack_string_update_pos(
  1484. data, pos+1, bk.encoding, lenlen=1)
  1485. else:
  1486. strg, newpos = unpack_unicode_update_pos(
  1487. data, pos+1, lenlen=1)
  1488. sz = newpos - pos
  1489. if blah: print(" sz=%d strg=%r" % (sz, strg), file=bk.logfile)
  1490. text = '"' + strg.replace('"', '""') + '"'
  1491. spush(Operand(oSTRG, None, LEAF_RANK, text))
  1492. elif opcode == 0x18: # tExtended
  1493. # new with BIFF 8
  1494. assert bv >= 80
  1495. # not in OOo docs, don't even know how to determine its length
  1496. raise FormulaError("tExtended token not implemented")
  1497. elif opcode == 0x19: # tAttr
  1498. subop, nc = unpack("<BH", data[pos+1:pos+4])
  1499. subname = tAttrNames.get(subop, "??Unknown??")
  1500. if subop == 0x04: # Choose
  1501. sz = nc * 2 + 6
  1502. elif subop == 0x10: # Sum (single arg)
  1503. sz = 4
  1504. if blah: print("tAttrSum", stack, file=bk.logfile)
  1505. assert len(stack) >= 1
  1506. aop = stack[-1]
  1507. otext = 'SUM(%s)' % aop.text
  1508. stack[-1] = Operand(oNUM, None, FUNC_RANK, otext)
  1509. else:
  1510. sz = 4
  1511. if blah:
  1512. print(" subop=%02xh subname=t%s sz=%d nc=%02xh" \
  1513. % (subop, subname, sz, nc), file=bk.logfile)
  1514. elif 0x1A <= opcode <= 0x1B: # tSheet, tEndSheet
  1515. assert bv < 50
  1516. raise FormulaError("tSheet & tEndsheet tokens not implemented")
  1517. elif 0x1C <= opcode <= 0x1F: # tErr, tBool, tInt, tNum
  1518. inx = opcode - 0x1C
  1519. nb = [1, 1, 2, 8][inx]
  1520. kind = [oERR, oBOOL, oNUM, oNUM][inx]
  1521. value, = unpack("<" + "BBHd"[inx], data[pos+1:pos+1+nb])
  1522. if inx == 2: # tInt
  1523. value = float(value)
  1524. text = str(value)
  1525. elif inx == 3: # tNum
  1526. text = str(value)
  1527. elif inx == 1: # tBool
  1528. text = ('FALSE', 'TRUE')[value]
  1529. else:
  1530. text = '"' +error_text_from_code[value] + '"'
  1531. spush(Operand(kind, None, LEAF_RANK, text))
  1532. else:
  1533. raise FormulaError("Unhandled opcode: 0x%02x" % opcode)
  1534. if sz <= 0:
  1535. raise FormulaError("Size not set for opcode 0x%02x" % opcode)
  1536. pos += sz
  1537. continue
  1538. if opcode == 0x00: # tArray
  1539. spush(unk_opnd)
  1540. elif opcode == 0x01: # tFunc
  1541. nb = 1 + int(bv >= 40)
  1542. funcx = unpack("<" + " BH"[nb], data[pos+1:pos+1+nb])[0]
  1543. func_attrs = func_defs.get(funcx, None)
  1544. if not func_attrs:
  1545. print("*** formula/tFunc unknown FuncID:%d" % funcx, file=bk.logfile)
  1546. spush(unk_opnd)
  1547. else:
  1548. func_name, nargs = func_attrs[:2]
  1549. if blah:
  1550. print(" FuncID=%d name=%s nargs=%d" \
  1551. % (funcx, func_name, nargs), file=bk.logfile)
  1552. assert len(stack) >= nargs
  1553. if nargs:
  1554. argtext = listsep.join([arg.text for arg in stack[-nargs:]])
  1555. otext = "%s(%s)" % (func_name, argtext)
  1556. del stack[-nargs:]
  1557. else:
  1558. otext = func_name + "()"
  1559. res = Operand(oUNK, None, FUNC_RANK, otext)
  1560. spush(res)
  1561. elif opcode == 0x02: #tFuncVar
  1562. nb = 1 + int(bv >= 40)
  1563. nargs, funcx = unpack("<B" + " BH"[nb], data[pos+1:pos+2+nb])
  1564. prompt, nargs = divmod(nargs, 128)
  1565. macro, funcx = divmod(funcx, 32768)
  1566. if blah:
  1567. print(" FuncID=%d nargs=%d macro=%d prompt=%d" \
  1568. % (funcx, nargs, macro, prompt), file=bk.logfile)
  1569. #### TODO #### if funcx == 255: # call add-in function
  1570. if funcx == 255:
  1571. func_attrs = ("CALL_ADDIN", 1, 30)
  1572. else:
  1573. func_attrs = func_defs.get(funcx, None)
  1574. if not func_attrs:
  1575. print("*** formula/tFuncVar unknown FuncID:%d" \
  1576. % funcx, file=bk.logfile)
  1577. spush(unk_opnd)
  1578. else:
  1579. func_name, minargs, maxargs = func_attrs[:3]
  1580. if blah:
  1581. print(" name: %r, min~max args: %d~%d" \
  1582. % (func_name, minargs, maxargs), file=bk.logfile)
  1583. assert minargs <= nargs <= maxargs
  1584. assert len(stack) >= nargs
  1585. assert len(stack) >= nargs
  1586. argtext = listsep.join([arg.text for arg in stack[-nargs:]])
  1587. otext = "%s(%s)" % (func_name, argtext)
  1588. res = Operand(oUNK, None, FUNC_RANK, otext)
  1589. del stack[-nargs:]
  1590. spush(res)
  1591. elif opcode == 0x03: #tName
  1592. tgtnamex = unpack("<H", data[pos+1:pos+3])[0] - 1
  1593. # Only change with BIFF version is number of trailing UNUSED bytes!
  1594. if blah: print(" tgtnamex=%d" % tgtnamex, file=bk.logfile)
  1595. tgtobj = bk.name_obj_list[tgtnamex]
  1596. if tgtobj.scope == -1:
  1597. otext = tgtobj.name
  1598. else:
  1599. otext = "%s!%s" % (bk._sheet_names[tgtobj.scope], tgtobj.name)
  1600. if blah:
  1601. print(" tName: setting text to", repr(otext), file=bk.logfile)
  1602. res = Operand(oUNK, None, LEAF_RANK, otext)
  1603. spush(res)
  1604. elif opcode == 0x04: # tRef
  1605. res = get_cell_addr(data, pos+1, bv, reldelta, browx, bcolx)
  1606. if blah: print(" ", res, file=bk.logfile)
  1607. rowx, colx, row_rel, col_rel = res
  1608. is_rel = row_rel or col_rel
  1609. if is_rel:
  1610. okind = oREL
  1611. else:
  1612. okind = oREF
  1613. otext = cellnamerel(rowx, colx, row_rel, col_rel, browx, bcolx, r1c1)
  1614. res = Operand(okind, None, LEAF_RANK, otext)
  1615. spush(res)
  1616. elif opcode == 0x05: # tArea
  1617. res1, res2 = get_cell_range_addr(
  1618. data, pos+1, bv, reldelta, browx, bcolx)
  1619. if blah: print(" ", res1, res2, file=bk.logfile)
  1620. rowx1, colx1, row_rel1, col_rel1 = res1
  1621. rowx2, colx2, row_rel2, col_rel2 = res2
  1622. coords = (rowx1, rowx2+1, colx1, colx2+1)
  1623. relflags = (row_rel1, row_rel2, col_rel1, col_rel2)
  1624. if sum(relflags): # relative
  1625. okind = oREL
  1626. else:
  1627. okind = oREF
  1628. if blah: print(" ", coords, relflags, file=bk.logfile)
  1629. otext = rangename2drel(coords, relflags, browx, bcolx, r1c1)
  1630. res = Operand(okind, None, LEAF_RANK, otext)
  1631. spush(res)
  1632. elif opcode == 0x06: # tMemArea
  1633. not_in_name_formula(op, oname)
  1634. elif opcode == 0x09: # tMemFunc
  1635. nb = unpack("<H", data[pos+1:pos+3])[0]
  1636. if blah: print(" %d bytes of cell ref formula" % nb, file=bk.logfile)
  1637. # no effect on stack
  1638. elif opcode == 0x0C: #tRefN
  1639. res = get_cell_addr(data, pos+1, bv, reldelta, browx, bcolx)
  1640. # note *ALL* tRefN usage has signed offset for relative addresses
  1641. any_rel = 1
  1642. if blah: print(" ", res, file=bk.logfile)
  1643. rowx, colx, row_rel, col_rel = res
  1644. is_rel = row_rel or col_rel
  1645. if is_rel:
  1646. okind = oREL
  1647. else:
  1648. okind = oREF
  1649. otext = cellnamerel(rowx, colx, row_rel, col_rel, browx, bcolx, r1c1)
  1650. res = Operand(okind, None, LEAF_RANK, otext)
  1651. spush(res)
  1652. elif opcode == 0x0D: #tAreaN
  1653. # res = get_cell_range_addr(data, pos+1, bv, reldelta, browx, bcolx)
  1654. # # note *ALL* tAreaN usage has signed offset for relative addresses
  1655. # any_rel = 1
  1656. # if blah: print >> bk.logfile, " ", res
  1657. res1, res2 = get_cell_range_addr(
  1658. data, pos+1, bv, reldelta, browx, bcolx)
  1659. if blah: print(" ", res1, res2, file=bk.logfile)
  1660. rowx1, colx1, row_rel1, col_rel1 = res1
  1661. rowx2, colx2, row_rel2, col_rel2 = res2
  1662. coords = (rowx1, rowx2+1, colx1, colx2+1)
  1663. relflags = (row_rel1, row_rel2, col_rel1, col_rel2)
  1664. if sum(relflags): # relative
  1665. okind = oREL
  1666. else:
  1667. okind = oREF
  1668. if blah: print(" ", coords, relflags, file=bk.logfile)
  1669. otext = rangename2drel(coords, relflags, browx, bcolx, r1c1)
  1670. res = Operand(okind, None, LEAF_RANK, otext)
  1671. spush(res)
  1672. elif opcode == 0x1A: # tRef3d
  1673. if bv >= 80:
  1674. res = get_cell_addr(data, pos+3, bv, reldelta, browx, bcolx)
  1675. refx = unpack("<H", data[pos+1:pos+3])[0]
  1676. shx1, shx2 = get_externsheet_local_range(bk, refx, blah)
  1677. else:
  1678. res = get_cell_addr(data, pos+15, bv, reldelta, browx, bcolx)
  1679. raw_extshtx, raw_shx1, raw_shx2 = \
  1680. unpack("<hxxxxxxxxhh", data[pos+1:pos+15])
  1681. if blah:
  1682. print("tRef3d", raw_extshtx, raw_shx1, raw_shx2, file=bk.logfile)
  1683. shx1, shx2 = get_externsheet_local_range_b57(
  1684. bk, raw_extshtx, raw_shx1, raw_shx2, blah)
  1685. rowx, colx, row_rel, col_rel = res
  1686. is_rel = row_rel or col_rel
  1687. any_rel = any_rel or is_rel
  1688. coords = (shx1, shx2+1, rowx, rowx+1, colx, colx+1)
  1689. any_err |= shx1 < -1
  1690. if blah: print(" ", coords, file=bk.logfile)
  1691. res = Operand(oUNK, None)
  1692. if is_rel:
  1693. relflags = (0, 0, row_rel, row_rel, col_rel, col_rel)
  1694. ref3d = Ref3D(coords + relflags)
  1695. res.kind = oREL
  1696. res.text = rangename3drel(bk, ref3d, browx, bcolx, r1c1)
  1697. else:
  1698. ref3d = Ref3D(coords)
  1699. res.kind = oREF
  1700. res.text = rangename3d(bk, ref3d)
  1701. res.rank = LEAF_RANK
  1702. res.value = None
  1703. spush(res)
  1704. elif opcode == 0x1B: # tArea3d
  1705. if bv >= 80:
  1706. res1, res2 = get_cell_range_addr(data, pos+3, bv, reldelta)
  1707. refx = unpack("<H", data[pos+1:pos+3])[0]
  1708. shx1, shx2 = get_externsheet_local_range(bk, refx, blah)
  1709. else:
  1710. res1, res2 = get_cell_range_addr(data, pos+15, bv, reldelta)
  1711. raw_extshtx, raw_shx1, raw_shx2 = \
  1712. unpack("<hxxxxxxxxhh", data[pos+1:pos+15])
  1713. if blah:
  1714. print("tArea3d", raw_extshtx, raw_shx1, raw_shx2, file=bk.logfile)
  1715. shx1, shx2 = get_externsheet_local_range_b57(
  1716. bk, raw_extshtx, raw_shx1, raw_shx2, blah)
  1717. any_err |= shx1 < -1
  1718. rowx1, colx1, row_rel1, col_rel1 = res1
  1719. rowx2, colx2, row_rel2, col_rel2 = res2
  1720. is_rel = row_rel1 or col_rel1 or row_rel2 or col_rel2
  1721. any_rel = any_rel or is_rel
  1722. coords = (shx1, shx2+1, rowx1, rowx2+1, colx1, colx2+1)
  1723. if blah: print(" ", coords, file=bk.logfile)
  1724. res = Operand(oUNK, None)
  1725. if is_rel:
  1726. relflags = (0, 0, row_rel1, row_rel2, col_rel1, col_rel2)
  1727. ref3d = Ref3D(coords + relflags)
  1728. res.kind = oREL
  1729. res.text = rangename3drel(bk, ref3d, browx, bcolx, r1c1)
  1730. else:
  1731. ref3d = Ref3D(coords)
  1732. res.kind = oREF
  1733. res.text = rangename3d(bk, ref3d)
  1734. res.rank = LEAF_RANK
  1735. spush(res)
  1736. elif opcode == 0x19: # tNameX
  1737. dodgy = 0
  1738. res = Operand(oUNK, None)
  1739. if bv >= 80:
  1740. refx, tgtnamex = unpack("<HH", data[pos+1:pos+5])
  1741. tgtnamex -= 1
  1742. origrefx = refx
  1743. else:
  1744. refx, tgtnamex = unpack("<hxxxxxxxxH", data[pos+1:pos+13])
  1745. tgtnamex -= 1
  1746. origrefx = refx
  1747. if refx > 0:
  1748. refx -= 1
  1749. elif refx < 0:
  1750. refx = -refx - 1
  1751. else:
  1752. dodgy = 1
  1753. if blah:
  1754. print(" origrefx=%d refx=%d tgtnamex=%d dodgy=%d" \
  1755. % (origrefx, refx, tgtnamex, dodgy), file=bk.logfile)
  1756. # if tgtnamex == namex:
  1757. # if blah: print >> bk.logfile, "!!!! Self-referential !!!!"
  1758. # dodgy = any_err = 1
  1759. if not dodgy:
  1760. if bv >= 80:
  1761. shx1, shx2 = get_externsheet_local_range(bk, refx, blah)
  1762. elif origrefx > 0:
  1763. shx1, shx2 = (-4, -4) # external ref
  1764. else:
  1765. exty = bk._externsheet_type_b57[refx]
  1766. if exty == 4: # non-specific sheet in own doc't
  1767. shx1, shx2 = (-1, -1) # internal, any sheet
  1768. else:
  1769. shx1, shx2 = (-666, -666)
  1770. okind = oUNK
  1771. ovalue = None
  1772. if shx1 == -5: # addin func name
  1773. okind = oSTRG
  1774. ovalue = bk.addin_func_names[tgtnamex]
  1775. otext = '"' + ovalue.replace('"', '""') + '"'
  1776. elif dodgy or shx1 < -1:
  1777. otext = "<<Name #%d in external(?) file #%d>>" \
  1778. % (tgtnamex, origrefx)
  1779. else:
  1780. tgtobj = bk.name_obj_list[tgtnamex]
  1781. if tgtobj.scope == -1:
  1782. otext = tgtobj.name
  1783. else:
  1784. otext = "%s!%s" \
  1785. % (bk._sheet_names[tgtobj.scope], tgtobj.name)
  1786. if blah:
  1787. print(" tNameX: setting text to", repr(res.text), file=bk.logfile)
  1788. res = Operand(okind, ovalue, LEAF_RANK, otext)
  1789. spush(res)
  1790. elif opcode in error_opcodes:
  1791. any_err = 1
  1792. spush(error_opnd)
  1793. else:
  1794. if blah:
  1795. print("FORMULA: /// Not handled yet: t" + oname, file=bk.logfile)
  1796. any_err = 1
  1797. if sz <= 0:
  1798. raise FormulaError("Fatal: token size is not positive")
  1799. pos += sz
  1800. any_rel = not not any_rel
  1801. if blah:
  1802. print("End of formula. level=%d any_rel=%d any_err=%d stack=%r" % \
  1803. (level, not not any_rel, any_err, stack), file=bk.logfile)
  1804. if len(stack) >= 2:
  1805. print("*** Stack has unprocessed args", file=bk.logfile)
  1806. print(file=bk.logfile)
  1807. if len(stack) != 1:
  1808. result = None
  1809. else:
  1810. result = stack[0].text
  1811. return result
  1812. #### under deconstruction ###
  1813. def dump_formula(bk, data, fmlalen, bv, reldelta, blah=0, isname=0):
  1814. if blah:
  1815. print("dump_formula", fmlalen, bv, len(data), file=bk.logfile)
  1816. hex_char_dump(data, 0, fmlalen, fout=bk.logfile)
  1817. assert bv >= 80 #### this function needs updating ####
  1818. sztab = szdict[bv]
  1819. pos = 0
  1820. stack = []
  1821. any_rel = 0
  1822. any_err = 0
  1823. spush = stack.append
  1824. while 0 <= pos < fmlalen:
  1825. op = BYTES_ORD(data[pos])
  1826. opcode = op & 0x1f
  1827. optype = (op & 0x60) >> 5
  1828. if optype:
  1829. opx = opcode + 32
  1830. else:
  1831. opx = opcode
  1832. oname = onames[opx] # + [" RVA"][optype]
  1833. sz = sztab[opx]
  1834. if blah:
  1835. print("Pos:%d Op:0x%02x Name:t%s Sz:%d opcode:%02xh optype:%02xh" \
  1836. % (pos, op, oname, sz, opcode, optype), file=bk.logfile)
  1837. if not optype:
  1838. if 0x01 <= opcode <= 0x02: # tExp, tTbl
  1839. # reference to a shared formula or table record
  1840. rowx, colx = unpack("<HH", data[pos+1:pos+5])
  1841. if blah: print(" ", (rowx, colx), file=bk.logfile)
  1842. elif opcode == 0x10: # tList
  1843. if blah: print("tList pre", stack, file=bk.logfile)
  1844. assert len(stack) >= 2
  1845. bop = stack.pop()
  1846. aop = stack.pop()
  1847. spush(aop + bop)
  1848. if blah: print("tlist post", stack, file=bk.logfile)
  1849. elif opcode == 0x11: # tRange
  1850. if blah: print("tRange pre", stack, file=bk.logfile)
  1851. assert len(stack) >= 2
  1852. bop = stack.pop()
  1853. aop = stack.pop()
  1854. assert len(aop) == 1
  1855. assert len(bop) == 1
  1856. result = do_box_funcs(tRangeFuncs, aop[0], bop[0])
  1857. spush(result)
  1858. if blah: print("tRange post", stack, file=bk.logfile)
  1859. elif opcode == 0x0F: # tIsect
  1860. if blah: print("tIsect pre", stack, file=bk.logfile)
  1861. assert len(stack) >= 2
  1862. bop = stack.pop()
  1863. aop = stack.pop()
  1864. assert len(aop) == 1
  1865. assert len(bop) == 1
  1866. result = do_box_funcs(tIsectFuncs, aop[0], bop[0])
  1867. spush(result)
  1868. if blah: print("tIsect post", stack, file=bk.logfile)
  1869. elif opcode == 0x19: # tAttr
  1870. subop, nc = unpack("<BH", data[pos+1:pos+4])
  1871. subname = tAttrNames.get(subop, "??Unknown??")
  1872. if subop == 0x04: # Choose
  1873. sz = nc * 2 + 6
  1874. else:
  1875. sz = 4
  1876. if blah: print(" subop=%02xh subname=t%s sz=%d nc=%02xh" % (subop, subname, sz, nc), file=bk.logfile)
  1877. elif opcode == 0x17: # tStr
  1878. if bv <= 70:
  1879. nc = BYTES_ORD(data[pos+1])
  1880. strg = data[pos+2:pos+2+nc] # left in 8-bit encoding
  1881. sz = nc + 2
  1882. else:
  1883. strg, newpos = unpack_unicode_update_pos(data, pos+1, lenlen=1)
  1884. sz = newpos - pos
  1885. if blah: print(" sz=%d strg=%r" % (sz, strg), file=bk.logfile)
  1886. else:
  1887. if sz <= 0:
  1888. print("**** Dud size; exiting ****", file=bk.logfile)
  1889. return
  1890. pos += sz
  1891. continue
  1892. if opcode == 0x00: # tArray
  1893. pass
  1894. elif opcode == 0x01: # tFunc
  1895. nb = 1 + int(bv >= 40)
  1896. funcx = unpack("<" + " BH"[nb], data[pos+1:pos+1+nb])
  1897. if blah: print(" FuncID=%d" % funcx, file=bk.logfile)
  1898. elif opcode == 0x02: #tFuncVar
  1899. nb = 1 + int(bv >= 40)
  1900. nargs, funcx = unpack("<B" + " BH"[nb], data[pos+1:pos+2+nb])
  1901. prompt, nargs = divmod(nargs, 128)
  1902. macro, funcx = divmod(funcx, 32768)
  1903. if blah: print(" FuncID=%d nargs=%d macro=%d prompt=%d" % (funcx, nargs, macro, prompt), file=bk.logfile)
  1904. elif opcode == 0x03: #tName
  1905. namex = unpack("<H", data[pos+1:pos+3])
  1906. # Only change with BIFF version is the number of trailing UNUSED bytes!!!
  1907. if blah: print(" namex=%d" % namex, file=bk.logfile)
  1908. elif opcode == 0x04: # tRef
  1909. res = get_cell_addr(data, pos+1, bv, reldelta)
  1910. if blah: print(" ", res, file=bk.logfile)
  1911. elif opcode == 0x05: # tArea
  1912. res = get_cell_range_addr(data, pos+1, bv, reldelta)
  1913. if blah: print(" ", res, file=bk.logfile)
  1914. elif opcode == 0x09: # tMemFunc
  1915. nb = unpack("<H", data[pos+1:pos+3])[0]
  1916. if blah: print(" %d bytes of cell ref formula" % nb, file=bk.logfile)
  1917. elif opcode == 0x0C: #tRefN
  1918. res = get_cell_addr(data, pos+1, bv, reldelta=1)
  1919. # note *ALL* tRefN usage has signed offset for relative addresses
  1920. any_rel = 1
  1921. if blah: print(" ", res, file=bk.logfile)
  1922. elif opcode == 0x0D: #tAreaN
  1923. res = get_cell_range_addr(data, pos+1, bv, reldelta=1)
  1924. # note *ALL* tAreaN usage has signed offset for relative addresses
  1925. any_rel = 1
  1926. if blah: print(" ", res, file=bk.logfile)
  1927. elif opcode == 0x1A: # tRef3d
  1928. refx = unpack("<H", data[pos+1:pos+3])[0]
  1929. res = get_cell_addr(data, pos+3, bv, reldelta)
  1930. if blah: print(" ", refx, res, file=bk.logfile)
  1931. rowx, colx, row_rel, col_rel = res
  1932. any_rel = any_rel or row_rel or col_rel
  1933. shx1, shx2 = get_externsheet_local_range(bk, refx, blah)
  1934. any_err |= shx1 < -1
  1935. coords = (shx1, shx2+1, rowx, rowx+1, colx, colx+1)
  1936. if blah: print(" ", coords, file=bk.logfile)
  1937. if optype == 1: spush([coords])
  1938. elif opcode == 0x1B: # tArea3d
  1939. refx = unpack("<H", data[pos+1:pos+3])[0]
  1940. res1, res2 = get_cell_range_addr(data, pos+3, bv, reldelta)
  1941. if blah: print(" ", refx, res1, res2, file=bk.logfile)
  1942. rowx1, colx1, row_rel1, col_rel1 = res1
  1943. rowx2, colx2, row_rel2, col_rel2 = res2
  1944. any_rel = any_rel or row_rel1 or col_rel1 or row_rel2 or col_rel2
  1945. shx1, shx2 = get_externsheet_local_range(bk, refx, blah)
  1946. any_err |= shx1 < -1
  1947. coords = (shx1, shx2+1, rowx1, rowx2+1, colx1, colx2+1)
  1948. if blah: print(" ", coords, file=bk.logfile)
  1949. if optype == 1: spush([coords])
  1950. elif opcode == 0x19: # tNameX
  1951. refx, namex = unpack("<HH", data[pos+1:pos+5])
  1952. if blah: print(" refx=%d namex=%d" % (refx, namex), file=bk.logfile)
  1953. elif opcode in error_opcodes:
  1954. any_err = 1
  1955. else:
  1956. if blah: print("FORMULA: /// Not handled yet: t" + oname, file=bk.logfile)
  1957. any_err = 1
  1958. if sz <= 0:
  1959. print("**** Dud size; exiting ****", file=bk.logfile)
  1960. return
  1961. pos += sz
  1962. if blah:
  1963. print("End of formula. any_rel=%d any_err=%d stack=%r" % \
  1964. (not not any_rel, any_err, stack), file=bk.logfile)
  1965. if len(stack) >= 2:
  1966. print("*** Stack has unprocessed args", file=bk.logfile)
  1967. # === Some helper functions for displaying cell references ===
  1968. # I'm aware of only one possibility of a sheet-relative component in
  1969. # a reference: a 2D reference located in the "current sheet".
  1970. # xlrd stores this internally with bounds of (0, 1, ...) and
  1971. # relative flags of (1, 1, ...). These functions display the
  1972. # sheet component as empty, just like Excel etc.
  1973. def rownamerel(rowx, rowxrel, browx=None, r1c1=0):
  1974. # if no base rowx is provided, we have to return r1c1
  1975. if browx is None:
  1976. r1c1 = True
  1977. if not rowxrel:
  1978. if r1c1:
  1979. return "R%d" % (rowx+1)
  1980. return "$%d" % (rowx+1)
  1981. if r1c1:
  1982. if rowx:
  1983. return "R[%d]" % rowx
  1984. return "R"
  1985. return "%d" % ((browx + rowx) % 65536 + 1)
  1986. def colnamerel(colx, colxrel, bcolx=None, r1c1=0):
  1987. # if no base colx is provided, we have to return r1c1
  1988. if bcolx is None:
  1989. r1c1 = True
  1990. if not colxrel:
  1991. if r1c1:
  1992. return "C%d" % (colx + 1)
  1993. return "$" + colname(colx)
  1994. if r1c1:
  1995. if colx:
  1996. return "C[%d]" % colx
  1997. return "C"
  1998. return colname((bcolx + colx) % 256)
  1999. ##
  2000. # Utility function: (5, 7) => 'H6'
  2001. def cellname(rowx, colx):
  2002. """ (5, 7) => 'H6' """
  2003. return "%s%d" % (colname(colx), rowx+1)
  2004. ##
  2005. # Utility function: (5, 7) => '$H$6'
  2006. def cellnameabs(rowx, colx, r1c1=0):
  2007. """ (5, 7) => '$H$6' or 'R8C6'"""
  2008. if r1c1:
  2009. return "R%dC%d" % (rowx+1, colx+1)
  2010. return "$%s$%d" % (colname(colx), rowx+1)
  2011. def cellnamerel(rowx, colx, rowxrel, colxrel, browx=None, bcolx=None, r1c1=0):
  2012. if not rowxrel and not colxrel:
  2013. return cellnameabs(rowx, colx, r1c1)
  2014. if (rowxrel and browx is None) or (colxrel and bcolx is None):
  2015. # must flip the whole cell into R1C1 mode
  2016. r1c1 = True
  2017. c = colnamerel(colx, colxrel, bcolx, r1c1)
  2018. r = rownamerel(rowx, rowxrel, browx, r1c1)
  2019. if r1c1:
  2020. return r + c
  2021. return c + r
  2022. ##
  2023. # Utility function: 7 => 'H', 27 => 'AB'
  2024. def colname(colx):
  2025. """ 7 => 'H', 27 => 'AB' """
  2026. alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  2027. if colx <= 25:
  2028. return alphabet[colx]
  2029. else:
  2030. xdiv26, xmod26 = divmod(colx, 26)
  2031. return alphabet[xdiv26 - 1] + alphabet[xmod26]
  2032. def rangename2d(rlo, rhi, clo, chi, r1c1=0):
  2033. """ (5, 20, 7, 10) => '$H$6:$J$20' """
  2034. if r1c1:
  2035. return
  2036. if rhi == rlo+1 and chi == clo+1:
  2037. return cellnameabs(rlo, clo, r1c1)
  2038. return "%s:%s" % (cellnameabs(rlo, clo, r1c1), cellnameabs(rhi-1, chi-1, r1c1))
  2039. def rangename2drel(rlo_rhi_clo_chi, rlorel_rhirel_clorel_chirel, browx=None, bcolx=None, r1c1=0):
  2040. rlo, rhi, clo, chi = rlo_rhi_clo_chi
  2041. rlorel, rhirel, clorel, chirel = rlorel_rhirel_clorel_chirel
  2042. if (rlorel or rhirel) and browx is None:
  2043. r1c1 = True
  2044. if (clorel or chirel) and bcolx is None:
  2045. r1c1 = True
  2046. return "%s:%s" % (
  2047. cellnamerel(rlo, clo, rlorel, clorel, browx, bcolx, r1c1),
  2048. cellnamerel(rhi-1, chi-1, rhirel, chirel, browx, bcolx, r1c1)
  2049. )
  2050. ##
  2051. # Utility function:
  2052. # <br /> Ref3D((1, 4, 5, 20, 7, 10)) => 'Sheet2:Sheet3!$H$6:$J$20'
  2053. def rangename3d(book, ref3d):
  2054. """ Ref3D(1, 4, 5, 20, 7, 10) => 'Sheet2:Sheet3!$H$6:$J$20'
  2055. (assuming Excel's default sheetnames) """
  2056. coords = ref3d.coords
  2057. return "%s!%s" % (
  2058. sheetrange(book, *coords[:2]),
  2059. rangename2d(*coords[2:6]))
  2060. ##
  2061. # Utility function:
  2062. # <br /> Ref3D(coords=(0, 1, -32, -22, -13, 13), relflags=(0, 0, 1, 1, 1, 1))
  2063. # R1C1 mode => 'Sheet1!R[-32]C[-13]:R[-23]C[12]'
  2064. # A1 mode => depends on base cell (browx, bcolx)
  2065. def rangename3drel(book, ref3d, browx=None, bcolx=None, r1c1=0):
  2066. coords = ref3d.coords
  2067. relflags = ref3d.relflags
  2068. shdesc = sheetrangerel(book, coords[:2], relflags[:2])
  2069. rngdesc = rangename2drel(coords[2:6], relflags[2:6], browx, bcolx, r1c1)
  2070. if not shdesc:
  2071. return rngdesc
  2072. return "%s!%s" % (shdesc, rngdesc)
  2073. def quotedsheetname(shnames, shx):
  2074. if shx >= 0:
  2075. shname = shnames[shx]
  2076. else:
  2077. shname = {
  2078. -1: "?internal; any sheet?",
  2079. -2: "internal; deleted sheet",
  2080. -3: "internal; macro sheet",
  2081. -4: "<<external>>",
  2082. }.get(shx, "?error %d?" % shx)
  2083. if "'" in shname:
  2084. return "'" + shname.replace("'", "''") + "'"
  2085. if " " in shname:
  2086. return "'" + shname + "'"
  2087. return shname
  2088. def sheetrange(book, slo, shi):
  2089. shnames = book.sheet_names()
  2090. shdesc = quotedsheetname(shnames, slo)
  2091. if slo != shi-1:
  2092. shdesc += ":" + quotedsheetname(shnames, shi-1)
  2093. return shdesc
  2094. def sheetrangerel(book, srange, srangerel):
  2095. slo, shi = srange
  2096. slorel, shirel = srangerel
  2097. if not slorel and not shirel:
  2098. return sheetrange(book, slo, shi)
  2099. assert (slo == 0 == shi-1) and slorel and shirel
  2100. return ""
  2101. # ==============================================================