[ PHPXref.com ] [ Generated: Sun Jul 20 17:06:29 2008 ] [ CRM-CTT 3.4.2 ]
[ Index ]     [ Variables ]     [ Functions ]     [ Classes ]     [ Constants ]     [ Statistics ]

title

Body

[close]

/ -> Parser.php (source)

   1  <?php
   2  /**

   3  *  Class for parsing Excel formulas

   4  *

   5  *  License Information:

   6  *

   7  *    Spreadsheet_Excel_Writer:  A library for generating Excel Spreadsheets

   8  *    Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com

   9  *

  10  *    This library is free software; you can redistribute it and/or

  11  *    modify it under the terms of the GNU Lesser General Public

  12  *    License as published by the Free Software Foundation; either

  13  *    version 2.1 of the License, or (at your option) any later version.

  14  *

  15  *    This library is distributed in the hope that it will be useful,

  16  *    but WITHOUT ANY WARRANTY; without even the implied warranty of

  17  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU

  18  *    Lesser General Public License for more details.

  19  *

  20  *    You should have received a copy of the GNU Lesser General Public

  21  *    License along with this library; if not, write to the Free Software

  22  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

  23  */
  24  
  25  /**

  26  * @const SPREADSHEET_EXCEL_WRITER_ADD token identifier for character "+"

  27  */
  28  define('SPREADSHEET_EXCEL_WRITER_ADD',"+");
  29  
  30  /**

  31  * @const SPREADSHEET_EXCEL_WRITER_SUB token identifier for character "-"

  32  */
  33  define('SPREADSHEET_EXCEL_WRITER_SUB',"-");
  34  
  35  /**

  36  * @const SPREADSHEET_EXCEL_WRITER_MUL token identifier for character "*"

  37  */
  38  define('SPREADSHEET_EXCEL_WRITER_MUL',"*");
  39  
  40  /**

  41  * @const SPREADSHEET_EXCEL_WRITER_DIV token identifier for character "/"

  42  */
  43  define('SPREADSHEET_EXCEL_WRITER_DIV',"/");
  44  
  45  /**

  46  * @const SPREADSHEET_EXCEL_WRITER_OPEN token identifier for character "("

  47  */
  48  define('SPREADSHEET_EXCEL_WRITER_OPEN',"(");
  49  
  50  /**

  51  * @const SPREADSHEET_EXCEL_WRITER_CLOSE token identifier for character ")"

  52  */
  53  define('SPREADSHEET_EXCEL_WRITER_CLOSE',")");
  54  
  55  /**

  56  * @const SPREADSHEET_EXCEL_WRITER_COMA token identifier for character ","

  57  */
  58  define('SPREADSHEET_EXCEL_WRITER_COMA',",");
  59  
  60  /**

  61  * @const SPREADSHEET_EXCEL_WRITER_SEMICOLON token identifier for character ";"

  62  */
  63  define('SPREADSHEET_EXCEL_WRITER_SEMICOLON',";");
  64  
  65  /**

  66  * @const SPREADSHEET_EXCEL_WRITER_GT token identifier for character ">"

  67  */
  68  define('SPREADSHEET_EXCEL_WRITER_GT',">");
  69  
  70  /**

  71  * @const SPREADSHEET_EXCEL_WRITER_LT token identifier for character "<"

  72  */
  73  define('SPREADSHEET_EXCEL_WRITER_LT',"<");
  74  
  75  /**

  76  * @const SPREADSHEET_EXCEL_WRITER_LE token identifier for character "<="

  77  */
  78  define('SPREADSHEET_EXCEL_WRITER_LE',"<=");
  79  
  80  /**

  81  * @const SPREADSHEET_EXCEL_WRITER_GE token identifier for character ">="

  82  */
  83  define('SPREADSHEET_EXCEL_WRITER_GE',">=");
  84  
  85  /**

  86  * @const SPREADSHEET_EXCEL_WRITER_EQ token identifier for character "="

  87  */
  88  define('SPREADSHEET_EXCEL_WRITER_EQ',"=");
  89  
  90  /**

  91  * @const SPREADSHEET_EXCEL_WRITER_NE token identifier for character "<>"

  92  */
  93  define('SPREADSHEET_EXCEL_WRITER_NE',"<>");
  94  
  95  
  96  require_once ('PEAR.php');
  97  
  98  /**

  99  * Class for parsing Excel formulas

 100  *

 101  * @author   Xavier Noguer <xnoguer@rezebra.com>

 102  * @category FileFormats

 103  * @package  Spreadsheet_Excel_Writer

 104  */
 105  
 106  class Spreadsheet_Excel_Writer_Parser extends PEAR
 107  {
 108      /**

 109      * The index of the character we are currently looking at

 110      * @var integer

 111      */
 112      var $_current_char;
 113  
 114      /**

 115      * The token we are working on.

 116      * @var string

 117      */
 118      var $_current_token;
 119  
 120      /**

 121      * The formula to parse

 122      * @var string

 123      */
 124      var $_formula;
 125  
 126      /**

 127      * The character ahead of the current char

 128      * @var string

 129      */
 130      var $_lookahead;
 131  
 132      /**

 133      * The parse tree to be generated

 134      * @var string

 135      */
 136      var $_parse_tree;
 137  
 138      /**

 139      * The byte order. 1 => big endian, 0 => little endian.

 140      * @var integer

 141      */
 142      var $_byte_order;
 143  
 144      /**

 145      * Array of external sheets

 146      * @var array

 147      */
 148      var $_ext_sheets;
 149  
 150      /**

 151      * Array of sheet references in the form of REF structures

 152      * @var array

 153      */
 154      var $_references;
 155  
 156      /**

 157      * The BIFF version for the workbook

 158      * @var integer

 159      */
 160      var $_BIFF_version;
 161  
 162      /**

 163      * The class constructor

 164      *

 165      * @param integer $byte_order The byte order (Little endian or Big endian) of the architecture

 166                                   (optional). 1 => big endian, 0 (default) little endian.

 167      */
 168      function Spreadsheet_Excel_Writer_Parser($byte_order, $biff_version)
 169      {
 170          $this->_current_char  = 0;
 171          $this->_BIFF_version  = $biff_version;
 172          $this->_current_token = '';       // The token we are working on.

 173          $this->_formula       = "";       // The formula to parse.

 174          $this->_lookahead     = '';       // The character ahead of the current char.

 175          $this->_parse_tree    = '';       // The parse tree to be generated.

 176          $this->_initializeHashes();      // Initialize the hashes: ptg's and function's ptg's

 177          $this->_byte_order = $byte_order; // Little Endian or Big Endian

 178          $this->_ext_sheets = array();
 179          $this->_references = array();
 180      }
 181      
 182      /**

 183      * Initialize the ptg and function hashes. 

 184      *

 185      * @access private

 186      */
 187      function _initializeHashes()
 188      {
 189          // The Excel ptg indices

 190          $this->ptg = array(
 191              'ptgExp'       => 0x01,
 192              'ptgTbl'       => 0x02,
 193              'ptgAdd'       => 0x03,
 194              'ptgSub'       => 0x04,
 195              'ptgMul'       => 0x05,
 196              'ptgDiv'       => 0x06,
 197              'ptgPower'     => 0x07,
 198              'ptgConcat'    => 0x08,
 199              'ptgLT'        => 0x09,
 200              'ptgLE'        => 0x0A,
 201              'ptgEQ'        => 0x0B,
 202              'ptgGE'        => 0x0C,
 203              'ptgGT'        => 0x0D,
 204              'ptgNE'        => 0x0E,
 205              'ptgIsect'     => 0x0F,
 206              'ptgUnion'     => 0x10,
 207              'ptgRange'     => 0x11,
 208              'ptgUplus'     => 0x12,
 209              'ptgUminus'    => 0x13,
 210              'ptgPercent'   => 0x14,
 211              'ptgParen'     => 0x15,
 212              'ptgMissArg'   => 0x16,
 213              'ptgStr'       => 0x17,
 214              'ptgAttr'      => 0x19,
 215              'ptgSheet'     => 0x1A,
 216              'ptgEndSheet'  => 0x1B,
 217              'ptgErr'       => 0x1C,
 218              'ptgBool'      => 0x1D,
 219              'ptgInt'       => 0x1E,
 220              'ptgNum'       => 0x1F,
 221              'ptgArray'     => 0x20,
 222              'ptgFunc'      => 0x21,
 223              'ptgFuncVar'   => 0x22,
 224              'ptgName'      => 0x23,
 225              'ptgRef'       => 0x24,
 226              'ptgArea'      => 0x25,
 227              'ptgMemArea'   => 0x26,
 228              'ptgMemErr'    => 0x27,
 229              'ptgMemNoMem'  => 0x28,
 230              'ptgMemFunc'   => 0x29,
 231              'ptgRefErr'    => 0x2A,
 232              'ptgAreaErr'   => 0x2B,
 233              'ptgRefN'      => 0x2C,
 234              'ptgAreaN'     => 0x2D,
 235              'ptgMemAreaN'  => 0x2E,
 236              'ptgMemNoMemN' => 0x2F,
 237              'ptgNameX'     => 0x39,
 238              'ptgRef3d'     => 0x3A,
 239              'ptgArea3d'    => 0x3B,
 240              'ptgRefErr3d'  => 0x3C,
 241              'ptgAreaErr3d' => 0x3D,
 242              'ptgArrayV'    => 0x40,
 243              'ptgFuncV'     => 0x41,
 244              'ptgFuncVarV'  => 0x42,
 245              'ptgNameV'     => 0x43,
 246              'ptgRefV'      => 0x44,
 247              'ptgAreaV'     => 0x45,
 248              'ptgMemAreaV'  => 0x46,
 249              'ptgMemErrV'   => 0x47,
 250              'ptgMemNoMemV' => 0x48,
 251              'ptgMemFuncV'  => 0x49,
 252              'ptgRefErrV'   => 0x4A,
 253              'ptgAreaErrV'  => 0x4B,
 254              'ptgRefNV'     => 0x4C,
 255              'ptgAreaNV'    => 0x4D,
 256              'ptgMemAreaNV' => 0x4E,
 257              'ptgMemNoMemN' => 0x4F,
 258              'ptgFuncCEV'   => 0x58,
 259              'ptgNameXV'    => 0x59,
 260              'ptgRef3dV'    => 0x5A,
 261              'ptgArea3dV'   => 0x5B,
 262              'ptgRefErr3dV' => 0x5C,
 263              'ptgAreaErr3d' => 0x5D,
 264              'ptgArrayA'    => 0x60,
 265              'ptgFuncA'     => 0x61,
 266              'ptgFuncVarA'  => 0x62,
 267              'ptgNameA'     => 0x63,
 268              'ptgRefA'      => 0x64,
 269              'ptgAreaA'     => 0x65,
 270              'ptgMemAreaA'  => 0x66,
 271              'ptgMemErrA'   => 0x67,
 272              'ptgMemNoMemA' => 0x68,
 273              'ptgMemFuncA'  => 0x69,
 274              'ptgRefErrA'   => 0x6A,
 275              'ptgAreaErrA'  => 0x6B,
 276              'ptgRefNA'     => 0x6C,
 277              'ptgAreaNA'    => 0x6D,
 278              'ptgMemAreaNA' => 0x6E,
 279              'ptgMemNoMemN' => 0x6F,
 280              'ptgFuncCEA'   => 0x78,
 281              'ptgNameXA'    => 0x79,
 282              'ptgRef3dA'    => 0x7A,
 283              'ptgArea3dA'   => 0x7B,
 284              'ptgRefErr3dA' => 0x7C,
 285              'ptgAreaErr3d' => 0x7D
 286              );
 287      
 288          // Thanks to Michael Meeks and Gnumeric for the initial arg values.

 289          //

 290          // The following hash was generated by "function_locale.pl" in the distro.

 291          // Refer to function_locale.pl for non-English function names.

 292          //

 293          // The array elements are as follow:

 294          // ptg:   The Excel function ptg code.

 295          // args:  The number of arguments that the function takes:

 296          //           >=0 is a fixed number of arguments.

 297          //           -1  is a variable  number of arguments.

 298          // class: The reference, value or array class of the function args.

 299          // vol:   The function is volatile.

 300          //

 301          $this->_functions = array(
 302                // function                  ptg  args  class  vol

 303                'COUNT'           => array(   0,   -1,    0,    0 ),
 304                'IF'              => array(   1,   -1,    1,    0 ),
 305                'ISNA'            => array(   2,    1,    1,    0 ),
 306                'ISERROR'         => array(   3,    1,    1,    0 ),
 307                'SUM'             => array(   4,   -1,    0,    0 ),
 308                'AVERAGE'         => array(   5,   -1,    0,    0 ),
 309                'MIN'             => array(   6,   -1,    0,    0 ),
 310                'MAX'             => array(   7,   -1,    0,    0 ),
 311                'ROW'             => array(   8,   -1,    0,    0 ),
 312                'COLUMN'          => array(   9,   -1,    0,    0 ),
 313                'NA'              => array(  10,    0,    0,    0 ),
 314                'NPV'             => array(  11,   -1,    1,    0 ),
 315                'STDEV'           => array(  12,   -1,    0,    0 ),
 316                'DOLLAR'          => array(  13,   -1,    1,    0 ),
 317                'FIXED'           => array(  14,   -1,    1,    0 ),
 318                'SIN'             => array(  15,    1,    1,    0 ),
 319                'COS'             => array(  16,    1,    1,    0 ),
 320                'TAN'             => array(  17,    1,    1,    0 ),
 321                'ATAN'            => array(  18,    1,    1,    0 ),
 322                'PI'              => array(  19,    0,    1,    0 ),
 323                'SQRT'            => array(  20,    1,    1,    0 ),
 324                'EXP'             => array(  21,    1,    1,    0 ),
 325                'LN'              => array(  22,    1,    1,    0 ),
 326                'LOG10'           => array(  23,    1,    1,    0 ),
 327                'ABS'             => array(  24,    1,    1,    0 ),
 328                'INT'             => array(  25,    1,    1,    0 ),
 329                'SIGN'            => array(  26,    1,    1,    0 ),
 330                'ROUND'           => array(  27,    2,    1,    0 ),
 331                'LOOKUP'          => array(  28,   -1,    0,    0 ),
 332                'INDEX'           => array(  29,   -1,    0,    1 ),
 333                'REPT'            => array(  30,    2,    1,    0 ),
 334                'MID'             => array(  31,    3,    1,    0 ),
 335                'LEN'             => array(  32,    1,    1,    0 ),
 336                'VALUE'           => array(  33,    1,    1,    0 ),
 337                'TRUE'            => array(  34,    0,    1,    0 ),
 338                'FALSE'           => array(  35,    0,    1,    0 ),
 339                'AND'             => array(  36,   -1,    0,    0 ),
 340                'OR'              => array(  37,   -1,    0,    0 ),
 341                'NOT'             => array(  38,    1,    1,    0 ),
 342                'MOD'             => array(  39,    2,    1,    0 ),
 343                'DCOUNT'          => array(  40,    3,    0,    0 ),
 344                'DSUM'            => array(  41,    3,    0,    0 ),
 345                'DAVERAGE'        => array(  42,    3,    0,    0 ),
 346                'DMIN'            => array(  43,    3,    0,    0 ),
 347                'DMAX'            => array(  44,    3,    0,    0 ),
 348                'DSTDEV'          => array(  45,    3,    0,    0 ),
 349                'VAR'             => array(  46,   -1,    0,    0 ),
 350                'DVAR'            => array(  47,    3,    0,    0 ),
 351                'TEXT'            => array(  48,    2,    1,    0 ),
 352                'LINEST'          => array(  49,   -1,    0,    0 ),
 353                'TREND'           => array(  50,   -1,    0,    0 ),
 354                'LOGEST'          => array(  51,   -1,    0,    0 ),
 355                'GROWTH'          => array(  52,   -1,    0,    0 ),
 356                'PV'              => array(  56,   -1,    1,    0 ),
 357                'FV'              => array(  57,   -1,    1,    0 ),
 358                'NPER'            => array(  58,   -1,    1,    0 ),
 359                'PMT'             => array(  59,   -1,    1,    0 ),
 360                'RATE'            => array(  60,   -1,    1,    0 ),
 361                'MIRR'            => array(  61,    3,    0,    0 ),
 362                'IRR'             => array(  62,   -1,    0,    0 ),
 363                'RAND'            => array(  63,    0,    1,    1 ),
 364                'MATCH'           => array(  64,   -1,    0,    0 ),
 365                'DATE'            => array(  65,    3,    1,    0 ),
 366                'TIME'            => array(  66,    3,    1,    0 ),
 367                'DAY'             => array(  67,    1,    1,    0 ),
 368                'MONTH'           => array(  68,    1,    1,    0 ),
 369                'YEAR'            => array(  69,    1,    1,    0 ),
 370                'WEEKDAY'         => array(  70,   -1,    1,    0 ),
 371                'HOUR'            => array(  71,    1,    1,    0 ),
 372                'MINUTE'          => array(  72,    1,    1,    0 ),
 373                'SECOND'          => array(  73,    1,    1,    0 ),
 374                'NOW'             => array(  74,    0,    1,    1 ),
 375                'AREAS'           => array(  75,    1,    0,    1 ),
 376                'ROWS'            => array(  76,    1,    0,    1 ),
 377                'COLUMNS'         => array(  77,    1,    0,    1 ),
 378                'OFFSET'          => array(  78,   -1,    0,    1 ),
 379                'SEARCH'          => array(  82,   -1,    1,    0 ),
 380                'TRANSPOSE'       => array(  83,    1,    1,    0 ),
 381                'TYPE'            => array(  86,    1,    1,    0 ),
 382                'ATAN2'           => array(  97,    2,    1,    0 ),
 383                'ASIN'            => array(  98,    1,    1,    0 ),
 384                'ACOS'            => array(  99,    1,    1,    0 ),
 385                'CHOOSE'          => array( 100,   -1,    1,    0 ),
 386                'HLOOKUP'         => array( 101,   -1,    0,    0 ),
 387                'VLOOKUP'         => array( 102,   -1,    0,    0 ),
 388                'ISREF'           => array( 105,    1,    0,    0 ),
 389                'LOG'             => array( 109,   -1,    1,    0 ),
 390                'CHAR'            => array( 111,    1,    1,    0 ),
 391                'LOWER'           => array( 112,    1,    1,    0 ),
 392                'UPPER'           => array( 113,    1,    1,    0 ),
 393                'PROPER'          => array( 114,    1,    1,    0 ),
 394                'LEFT'            => array( 115,   -1,    1,    0 ),
 395                'RIGHT'           => array( 116,   -1,    1,    0 ),
 396                'EXACT'           => array( 117,    2,    1,    0 ),
 397                'TRIM'            => array( 118,    1,    1,    0 ),
 398                'REPLACE'         => array( 119,    4,    1,    0 ),
 399                'SUBSTITUTE'      => array( 120,   -1,    1,    0 ),
 400                'CODE'            => array( 121,    1,    1,    0 ),
 401                'FIND'            => array( 124,   -1,    1,    0 ),
 402                'CELL'            => array( 125,   -1,    0,    1 ),
 403                'ISERR'           => array( 126,    1,    1,    0 ),
 404                'ISTEXT'          => array( 127,    1,    1,    0 ),
 405                'ISNUMBER'        => array( 128,    1,    1,    0 ),
 406                'ISBLANK'         => array( 129,    1,    1,    0 ),
 407                'T'               => array( 130,    1,    0,    0 ),
 408                'N'               => array( 131,    1,    0,    0 ),
 409                'DATEVALUE'       => array( 140,    1,    1,    0 ),
 410                'TIMEVALUE'       => array( 141,    1,    1,    0 ),
 411                'SLN'             => array( 142,    3,    1,    0 ),
 412                'SYD'             => array( 143,    4,    1,    0 ),
 413                'DDB'             => array( 144,   -1,    1,    0 ),
 414                'INDIRECT'        => array( 148,   -1,    1,    1 ),
 415                'CALL'            => array( 150,   -1,    1,    0 ),
 416                'CLEAN'           => array( 162,    1,    1,    0 ),
 417                'MDETERM'         => array( 163,    1,    2,    0 ),
 418                'MINVERSE'        => array( 164,    1,    2,    0 ),
 419                'MMULT'           => array( 165,    2,    2,    0 ),
 420                'IPMT'            => array( 167,   -1,    1,    0 ),
 421                'PPMT'            => array( 168,   -1,    1,    0 ),
 422                'COUNTA'          => array( 169,   -1,    0,    0 ),
 423                'PRODUCT'         => array( 183,   -1,    0,    0 ),
 424                'FACT'            => array( 184,    1,    1,    0 ),
 425                'DPRODUCT'        => array( 189,    3,    0,    0 ),
 426                'ISNONTEXT'       => array( 190,    1,    1,    0 ),
 427                'STDEVP'          => array( 193,   -1,    0,    0 ),
 428                'VARP'            => array( 194,   -1,    0,    0 ),
 429                'DSTDEVP'         => array( 195,    3,    0,    0 ),
 430                'DVARP'           => array( 196,    3,    0,    0 ),
 431                'TRUNC'           => array( 197,   -1,    1,    0 ),
 432                'ISLOGICAL'       => array( 198,    1,    1,    0 ),
 433                'DCOUNTA'         => array( 199,    3,    0,    0 ),
 434                'ROUNDUP'         => array( 212,    2,    1,    0 ),
 435                'ROUNDDOWN'       => array( 213,    2,    1,    0 ),
 436                'RANK'            => array( 216,   -1,    0,    0 ),
 437                'ADDRESS'         => array( 219,   -1,    1,    0 ),
 438                'DAYS360'         => array( 220,   -1,    1,    0 ),
 439                'TODAY'           => array( 221,    0,    1,    1 ),
 440                'VDB'             => array( 222,   -1,    1,    0 ),
 441                'MEDIAN'          => array( 227,   -1,    0,    0 ),
 442                'SUMPRODUCT'      => array( 228,   -1,    2,    0 ),
 443                'SINH'            => array( 229,    1,    1,    0 ),
 444                'COSH'            => array( 230,    1,    1,    0 ),
 445                'TANH'            => array( 231,    1,    1,    0 ),
 446                'ASINH'           => array( 232,    1,    1,    0 ),
 447                'ACOSH'           => array( 233,    1,    1,    0 ),
 448                'ATANH'           => array( 234,    1,    1,    0 ),
 449                'DGET'            => array( 235,    3,    0,    0 ),
 450                'INFO'            => array( 244,    1,    1,    1 ),
 451                'DB'              => array( 247,   -1,    1,    0 ),
 452                'FREQUENCY'       => array( 252,    2,    0,    0 ),
 453                'ERROR.TYPE'      => array( 261,    1,    1,    0 ),
 454                'REGISTER.ID'     => array( 267,   -1,    1,    0 ),
 455                'AVEDEV'          => array( 269,   -1,    0,    0 ),
 456                'BETADIST'        => array( 270,   -1,    1,    0 ),
 457                'GAMMALN'         => array( 271,    1,    1,    0 ),
 458                'BETAINV'         => array( 272,   -1,    1,    0 ),
 459                'BINOMDIST'       => array( 273,    4,    1,    0 ),
 460                'CHIDIST'         => array( 274,    2,    1,    0 ),
 461                'CHIINV'          => array( 275,    2,    1,    0 ),
 462                'COMBIN'          => array( 276,    2,    1,    0 ),
 463                'CONFIDENCE'      => array( 277,    3,    1,    0 ),
 464                'CRITBINOM'       => array( 278,    3,    1,    0 ),
 465                'EVEN'            => array( 279,    1,    1,    0 ),
 466                'EXPONDIST'       => array( 280,    3,    1,    0 ),
 467                'FDIST'           => array( 281,    3,    1,    0 ),
 468                'FINV'            => array( 282,    3,    1,    0 ),
 469                'FISHER'          => array( 283,    1,    1,    0 ),
 470                'FISHERINV'       => array( 284,    1,    1,    0 ),
 471                'FLOOR'           => array( 285,    2,    1,    0 ),
 472                'GAMMADIST'       => array( 286,    4,    1,    0 ),
 473                'GAMMAINV'        => array( 287,    3,    1,    0 ),
 474                'CEILING'         => array( 288,    2,    1,    0 ),
 475                'HYPGEOMDIST'     => array( 289,    4,    1,    0 ),
 476                'LOGNORMDIST'     => array( 290,    3,    1,    0 ),
 477                'LOGINV'          => array( 291,    3,    1,    0 ),
 478                'NEGBINOMDIST'    => array( 292,    3,    1,    0 ),
 479                'NORMDIST'        => array( 293,    4,    1,    0 ),
 480                'NORMSDIST'       => array( 294,    1,    1,    0 ),
 481                'NORMINV'         => array( 295,    3,    1,    0 ),
 482                'NORMSINV'        => array( 296,    1,    1,    0 ),
 483                'STANDARDIZE'     => array( 297,    3,    1,    0 ),
 484                'ODD'             => array( 298,    1,    1,    0 ),
 485                'PERMUT'          => array( 299,    2,    1,    0 ),
 486                'POISSON'         => array( 300,    3,    1,    0 ),
 487                'TDIST'           => array( 301,    3,    1,    0 ),
 488                'WEIBULL'         => array( 302,    4,    1,    0 ),
 489                'SUMXMY2'         => array( 303,    2,    2,    0 ),
 490                'SUMX2MY2'        => array( 304,    2,    2,    0 ),
 491                'SUMX2PY2'        => array( 305,    2,    2,    0 ),
 492                'CHITEST'         => array( 306,    2,    2,    0 ),
 493                'CORREL'          => array( 307,    2,    2,    0 ),
 494                'COVAR'           => array( 308,    2,    2,    0 ),
 495                'FORECAST'        => array( 309,    3,    2,    0 ),
 496                'FTEST'           => array( 310,    2,    2,    0 ),
 497                'INTERCEPT'       => array( 311,    2,    2,    0 ),
 498                'PEARSON'         => array( 312,    2,    2,    0 ),
 499                'RSQ'             => array( 313,    2,    2,    0 ),
 500                'STEYX'           => array( 314,    2,    2,    0 ),
 501                'SLOPE'           => array( 315,    2,    2,    0 ),
 502                'TTEST'           => array( 316,    4,    2,    0 ),
 503                'PROB'            => array( 317,   -1,    2,    0 ),
 504                'DEVSQ'           => array( 318,   -1,    0,    0 ),
 505                'GEOMEAN'         => array( 319,   -1,    0,    0 ),
 506                'HARMEAN'         => array( 320,   -1,    0,    0 ),
 507                'SUMSQ'           => array( 321,   -1,    0,    0 ),
 508                'KURT'            => array( 322,   -1,    0,    0 ),
 509                'SKEW'            => array( 323,   -1,    0,    0 ),
 510                'ZTEST'           => array( 324,   -1,    0,    0 ),
 511                'LARGE'           => array( 325,    2,    0,    0 ),
 512                'SMALL'           => array( 326,    2,    0,    0 ),
 513                'QUARTILE'        => array( 327,    2,    0,    0 ),
 514                'PERCENTILE'      => array( 328,    2,    0,    0 ),
 515                'PERCENTRANK'     => array( 329,   -1,    0,    0 ),
 516                'MODE'            => array( 330,   -1,    2,    0 ),
 517                'TRIMMEAN'        => array( 331,    2,    0,    0 ),
 518                'TINV'            => array( 332,    2,    1,    0 ),
 519                'CONCATENATE'     => array( 336,   -1,    1,    0 ),
 520                'POWER'           => array( 337,    2,    1,    0 ),
 521                'RADIANS'         => array( 342,    1,    1,    0 ),
 522                'DEGREES'         => array( 343,    1,    1,    0 ),
 523                'SUBTOTAL'        => array( 344,   -1,    0,    0 ),
 524                'SUMIF'           => array( 345,   -1,    0,    0 ),
 525                'COUNTIF'         => array( 346,    2,    0,    0 ),
 526                'COUNTBLANK'      => array( 347,    1,    0,    0 ),
 527                'ROMAN'           => array( 354,   -1,    1,    0 )
 528                );
 529      }
 530      
 531      /**

 532      * Convert a token to the proper ptg value.

 533      *

 534      * @access private

 535      * @param mixed $token The token to convert.

 536      * @return mixed the converted token on success. PEAR_Error if the token

 537      *               is not recognized

 538      */
 539      function _convert($token)
 540      {
 541          if (preg_match("/^\"[^\"]{0,255}\"$/", $token))
 542          {
 543              return $this->_convertString($token);
 544          }
 545          elseif (is_numeric($token))
 546          {
 547              return $this->_convertNumber($token);
 548          }
 549          // match references like A1 or $A$1

 550          elseif (preg_match('/^\$?([A-Ia-i]?[A-Za-z])\$?(\d+)$/',$token))
 551          {
 552              return $this->_convertRef2d($token);
 553          }
 554          // match external references like Sheet1!A1 or Sheet1:Sheet2!A1

 555          elseif (preg_match("/^\w+(\:\w+)?\![A-Ia-i]?[A-Za-z](\d+)$/",$token))
 556          {
 557              return $this->_convertRef3d($token);
 558          }
 559          // match external references like Sheet1!A1 or Sheet1:Sheet2!A1

 560          elseif (preg_match("/^'\w+(\:\w+)?'\![A-Ia-i]?[A-Za-z](\d+)$/",$token))
 561          {
 562              return $this->_convertRef3d($token);
 563          }
 564          // match ranges like A1:B2

 565          elseif (preg_match("/^(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)\:(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)$/",$token))
 566          {
 567              return $this->_convertRange2d($token);
 568          }
 569          // match ranges like A1..B2

 570          elseif (preg_match("/^(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)\.\.(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)$/",$token))
 571          {
 572              return $this->_convertRange2d($token);
 573          }
 574          // match external ranges like Sheet1!A1 or Sheet1:Sheet2!A1:B2

 575          elseif (preg_match("/^\w+(\:\w+)?\!([A-Ia-i]?[A-Za-z])?(\d+)\:([A-Ia-i]?[A-Za-z])?(\d+)$/",$token))
 576          {
 577              return $this->_convertRange3d($token);
 578          }
 579          // match external ranges like 'Sheet1'!A1 or 'Sheet1:Sheet2'!A1:B2

 580          elseif (preg_match("/^'\w+(\:\w+)?'\!([A-Ia-i]?[A-Za-z])?(\d+)\:([A-Ia-i]?[A-Za-z])?(\d+)$/",$token))
 581          {
 582              return $this->_convertRange3d($token);
 583          }
 584          elseif (isset($this->ptg[$token])) // operators (including parentheses)
 585          {
 586              return pack("C", $this->ptg[$token]);
 587          }
 588          // commented so argument number can be processed correctly. See toReversePolish().

 589          /*elseif (preg_match("/[A-Z0-9\xc0-\xdc\.]+/",$token))

 590          {

 591              return($this->_convertFunction($token,$this->_func_args));

 592          }*/
 593          // if it's an argument, ignore the token (the argument remains)

 594          elseif ($token == 'arg')
 595          {
 596              return '';
 597          }
 598          // TODO: use real error codes

 599          return $this->raiseError("Unknown token $token");
 600      }
 601      
 602      /**

 603      * Convert a number token to ptgInt or ptgNum

 604      *

 605      * @access private

 606      * @param mixed $num an integer or double for conversion to its ptg value

 607      */
 608      function _convertNumber($num)
 609      {
 610          // Integer in the range 0..2**16-1

 611          if ((preg_match("/^\d+$/",$num)) and ($num <= 65535)) {
 612              return pack("Cv", $this->ptg['ptgInt'], $num);
 613          }
 614          else // A float
 615          {
 616              if ($this->_byte_order) { // if it's Big Endian
 617                  $num = strrev($num);
 618              }
 619              return pack("Cd", $this->ptg['ptgNum'], $num);
 620          }
 621      }
 622      
 623      /**

 624      * Convert a string token to ptgStr

 625      *

 626      * @access private

 627      * @param string $string A string for conversion to its ptg value. 

 628      * @return mixed the converted token on success. PEAR_Error if the string

 629      *               is longer than 255 characters.

 630      */
 631      function _convertString($string)
 632      {
 633          // chop away beggining and ending quotes

 634          $string = substr($string, 1, strlen($string) - 2);
 635          if (strlen($string) > 255) {
 636              return $this->raiseError("String is too long");
 637          }
 638          if ($this->_BIFF_version == 0x0500) {
 639              return pack("CC", $this->ptg['ptgStr'], strlen($string)).$string;
 640          }
 641          elseif ($this->_BIFF_version == 0x0600) {
 642              $encoding = 0;   // TODO: Unicode support

 643              return pack("CCC", $this->ptg['ptgStr'], strlen($string), $encoding).$string;
 644          }
 645      }
 646   
 647      /**

 648      * Convert a function to a ptgFunc or ptgFuncVarV depending on the number of

 649      * args that it takes.

 650      *

 651      * @access private

 652      * @param string  $token    The name of the function for convertion to ptg value.

 653      * @param integer $num_args The number of arguments the function receives.

 654      * @return string The packed ptg for the function

 655      */
 656      function _convertFunction($token, $num_args)
 657      {
 658          $args     = $this->_functions[$token][1];
 659          $volatile = $this->_functions[$token][3];
 660      
 661          // Fixed number of args eg. TIME($i,$j,$k).

 662          if ($args >= 0) {
 663              return pack("Cv", $this->ptg['ptgFuncV'], $this->_functions[$token][0]);
 664          }
 665          // Variable number of args eg. SUM($i,$j,$k, ..).

 666          if ($args == -1) {
 667              return pack("CCv", $this->ptg['ptgFuncVarV'], $num_args, $this->_functions[$token][0]);
 668          }
 669      }
 670      
 671      /**

 672      * Convert an Excel range such as A1:D4 to a ptgRefV.

 673      *

 674      * @access private

 675      * @param string $range An Excel range in the A1:A2 or A1..A2 format.

 676      */
 677      function _convertRange2d($range)
 678      {
 679          $class = 2; // as far as I know, this is magick.

 680      
 681          // Split the range into 2 cell refs

 682          if (preg_match("/^([A-Ia-i]?[A-Za-z])(\d+)\:([A-Ia-i]?[A-Za-z])(\d+)$/",$range)) {
 683              list($cell1, $cell2) = split(':', $range);
 684          }
 685          elseif (preg_match("/^([A-Ia-i]?[A-Za-z])(\d+)\.\.([A-Ia-i]?[A-Za-z])(\d+)$/",$range)) {
 686              list($cell1, $cell2) = split('\.\.', $range);
 687          
 688          }
 689          else {
 690              // TODO: use real error codes

 691              return $this->raiseError("Unknown range separator", 0, PEAR_ERROR_DIE);
 692          }
 693      
 694          // Convert the cell references

 695          $cell_array1 = $this->_cellToPackedRowcol($cell1);
 696          if (PEAR::isError($cell_array1)) {
 697              return $cell_array1;
 698          }
 699          list($row1, $col1) = $cell_array1;
 700          $cell_array2 = $this->_cellToPackedRowcol($cell2);
 701          if (PEAR::isError($cell_array2)) {
 702              return $cell_array2;
 703          }
 704          list($row2, $col2) = $cell_array2;
 705      
 706          // The ptg value depends on the class of the ptg.

 707          if ($class == 0) {
 708              $ptgArea = pack("C", $this->ptg['ptgArea']);
 709          }
 710          elseif ($class == 1) {
 711              $ptgArea = pack("C", $this->ptg['ptgAreaV']);
 712          }
 713          elseif ($class == 2) {
 714              $ptgArea = pack("C", $this->ptg['ptgAreaA']);
 715          }
 716          else {
 717              // TODO: use real error codes

 718              return $this->raiseError("Unknown class $class", 0, PEAR_ERROR_DIE);
 719          }
 720          return $ptgArea . $row1 . $row2 . $col1. $col2;
 721      }
 722   
 723      /**

 724      * Convert an Excel 3d range such as "Sheet1!A1:D4" or "Sheet1:Sheet2!A1:D4" to

 725      * a ptgArea3d.

 726      *

 727      * @access private

 728      * @param string $token An Excel range in the Sheet1!A1:A2 format.

 729      * @return mixed The packed ptgArea3d token on success, PEAR_Error on failure.

 730      */
 731      function _convertRange3d($token)
 732      {
 733          $class = 2; // as far as I know, this is magick.

 734   
 735          // Split the ref at the ! symbol

 736          list($ext_ref, $range) = split('!', $token);
 737   
 738          // Convert the external reference part (different for BIFF8)

 739          if ($this->_BIFF_version == 0x0500) {
 740              $ext_ref = $this->_packExtRef($ext_ref);
 741              if (PEAR::isError($ext_ref)) {
 742                  return $ext_ref;
 743              }
 744          }
 745          elseif ($this->_BIFF_version == 0x0600) {
 746               $ext_ref = $this->_getRefIndex($ext_ref);
 747               if (PEAR::isError($ext_ref)) {
 748                   return $ext_ref;
 749               }
 750          }
 751  
 752          // Split the range into 2 cell refs

 753          list($cell1, $cell2) = split(':', $range);
 754   
 755          // Convert the cell references

 756          if (preg_match("/^(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)$/", $cell1))
 757          {
 758              $cell_array1 = $this->_cellToPackedRowcol($cell1);
 759              if (PEAR::isError($cell_array1)) {
 760                  return $cell_array1;
 761              }
 762              list($row1, $col1) = $cell_array1;
 763              $cell_array2 = $this->_cellToPackedRowcol($cell2);
 764              if (PEAR::