You are here

class PHPExcel_Writer_Excel5_Parser in Loft Data Grids 6.2

Same name and namespace in other branches
  1. 7.2 vendor/phpoffice/phpexcel/Classes/PHPExcel/Writer/Excel5/Parser.php \PHPExcel_Writer_Excel5_Parser

PHPExcel_Writer_Excel5_Parser

@category PHPExcel @package PHPExcel_Writer_Excel5 @copyright Copyright (c) 2006 - 2014 PHPExcel (http://www.codeplex.com/PHPExcel)

Hierarchy

Expanded class hierarchy of PHPExcel_Writer_Excel5_Parser

File

vendor/phpoffice/phpexcel/Classes/PHPExcel/Writer/Excel5/Parser.php, line 60

View source
class PHPExcel_Writer_Excel5_Parser {

  /**	Constants				*/

  // Sheet title in unquoted form
  // Invalid sheet title characters cannot occur in the sheet title:
  // 		*:/\?[]
  // Moreover, there are valid sheet title characters that cannot occur in unquoted form (there may be more?)
  // +-% '^&<>=,;#()"{}
  const REGEX_SHEET_TITLE_UNQUOTED = '[^\\*\\:\\/\\\\\\?\\[\\]\\+\\-\\% \\\'\\^\\&\\<\\>\\=\\,\\;\\#\\(\\)\\"\\{\\}]+';

  // Sheet title in quoted form (without surrounding quotes)
  // Invalid sheet title characters cannot occur in the sheet title:
  // *:/\?[]					(usual invalid sheet title characters)
  // Single quote is represented as a pair ''
  const REGEX_SHEET_TITLE_QUOTED = '(([^\\*\\:\\/\\\\\\?\\[\\]\\\'])+|(\\\'\\\')+)+';

  /**
   * The index of the character we are currently looking at
   * @var integer
   */
  public $_current_char;

  /**
   * The token we are working on.
   * @var string
   */
  public $_current_token;

  /**
   * The formula to parse
   * @var string
   */
  public $_formula;

  /**
   * The character ahead of the current char
   * @var string
   */
  public $_lookahead;

  /**
   * The parse tree to be generated
   * @var string
   */
  public $_parse_tree;

  /**
   * Array of external sheets
   * @var array
   */
  public $_ext_sheets;

  /**
   * Array of sheet references in the form of REF structures
   * @var array
   */
  public $_references;

  /**
   * The class constructor
   *
   */
  public function __construct() {
    $this->_current_char = 0;
    $this->_current_token = '';

    // The token we are working on.
    $this->_formula = '';

    // The formula to parse.
    $this->_lookahead = '';

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

    // The parse tree to be generated.
    $this
      ->_initializeHashes();

    // Initialize the hashes: ptg's and function's ptg's
    $this->_ext_sheets = array();
    $this->_references = array();
  }

  /**
   * Initialize the ptg and function hashes.
   *
   * @access private
   */
  function _initializeHashes() {

    // The Excel ptg indices
    $this->ptg = array(
      'ptgExp' => 0x1,
      'ptgTbl' => 0x2,
      'ptgAdd' => 0x3,
      'ptgSub' => 0x4,
      'ptgMul' => 0x5,
      'ptgDiv' => 0x6,
      'ptgPower' => 0x7,
      'ptgConcat' => 0x8,
      'ptgLT' => 0x9,
      'ptgLE' => 0xa,
      'ptgEQ' => 0xb,
      'ptgGE' => 0xc,
      'ptgGT' => 0xd,
      'ptgNE' => 0xe,
      'ptgIsect' => 0xf,
      'ptgUnion' => 0x10,
      'ptgRange' => 0x11,
      'ptgUplus' => 0x12,
      'ptgUminus' => 0x13,
      'ptgPercent' => 0x14,
      'ptgParen' => 0x15,
      'ptgMissArg' => 0x16,
      'ptgStr' => 0x17,
      'ptgAttr' => 0x19,
      'ptgSheet' => 0x1a,
      'ptgEndSheet' => 0x1b,
      'ptgErr' => 0x1c,
      'ptgBool' => 0x1d,
      'ptgInt' => 0x1e,
      'ptgNum' => 0x1f,
      'ptgArray' => 0x20,
      'ptgFunc' => 0x21,
      'ptgFuncVar' => 0x22,
      'ptgName' => 0x23,
      'ptgRef' => 0x24,
      'ptgArea' => 0x25,
      'ptgMemArea' => 0x26,
      'ptgMemErr' => 0x27,
      'ptgMemNoMem' => 0x28,
      'ptgMemFunc' => 0x29,
      'ptgRefErr' => 0x2a,
      'ptgAreaErr' => 0x2b,
      'ptgRefN' => 0x2c,
      'ptgAreaN' => 0x2d,
      'ptgMemAreaN' => 0x2e,
      'ptgMemNoMemN' => 0x2f,
      'ptgNameX' => 0x39,
      'ptgRef3d' => 0x3a,
      'ptgArea3d' => 0x3b,
      'ptgRefErr3d' => 0x3c,
      'ptgAreaErr3d' => 0x3d,
      'ptgArrayV' => 0x40,
      'ptgFuncV' => 0x41,
      'ptgFuncVarV' => 0x42,
      'ptgNameV' => 0x43,
      'ptgRefV' => 0x44,
      'ptgAreaV' => 0x45,
      'ptgMemAreaV' => 0x46,
      'ptgMemErrV' => 0x47,
      'ptgMemNoMemV' => 0x48,
      'ptgMemFuncV' => 0x49,
      'ptgRefErrV' => 0x4a,
      'ptgAreaErrV' => 0x4b,
      'ptgRefNV' => 0x4c,
      'ptgAreaNV' => 0x4d,
      'ptgMemAreaNV' => 0x4e,
      'ptgMemNoMemN' => 0x4f,
      'ptgFuncCEV' => 0x58,
      'ptgNameXV' => 0x59,
      'ptgRef3dV' => 0x5a,
      'ptgArea3dV' => 0x5b,
      'ptgRefErr3dV' => 0x5c,
      'ptgAreaErr3d' => 0x5d,
      'ptgArrayA' => 0x60,
      'ptgFuncA' => 0x61,
      'ptgFuncVarA' => 0x62,
      'ptgNameA' => 0x63,
      'ptgRefA' => 0x64,
      'ptgAreaA' => 0x65,
      'ptgMemAreaA' => 0x66,
      'ptgMemErrA' => 0x67,
      'ptgMemNoMemA' => 0x68,
      'ptgMemFuncA' => 0x69,
      'ptgRefErrA' => 0x6a,
      'ptgAreaErrA' => 0x6b,
      'ptgRefNA' => 0x6c,
      'ptgAreaNA' => 0x6d,
      'ptgMemAreaNA' => 0x6e,
      'ptgMemNoMemN' => 0x6f,
      'ptgFuncCEA' => 0x78,
      'ptgNameXA' => 0x79,
      'ptgRef3dA' => 0x7a,
      'ptgArea3dA' => 0x7b,
      'ptgRefErr3dA' => 0x7c,
      'ptgAreaErr3d' => 0x7d,
    );

    // Thanks to Michael Meeks and Gnumeric for the initial arg values.
    //
    // The following hash was generated by "function_locale.pl" in the distro.
    // Refer to function_locale.pl for non-English function names.
    //
    // The array elements are as follow:
    // ptg:   The Excel function ptg code.
    // args:  The number of arguments that the function takes:
    //           >=0 is a fixed number of arguments.
    //           -1  is a variable  number of arguments.
    // class: The reference, value or array class of the function args.
    // vol:   The function is volatile.
    //
    $this->_functions = array(
      // function                  ptg  args  class  vol
      'COUNT' => array(
        0,
        -1,
        0,
        0,
      ),
      'IF' => array(
        1,
        -1,
        1,
        0,
      ),
      'ISNA' => array(
        2,
        1,
        1,
        0,
      ),
      'ISERROR' => array(
        3,
        1,
        1,
        0,
      ),
      'SUM' => array(
        4,
        -1,
        0,
        0,
      ),
      'AVERAGE' => array(
        5,
        -1,
        0,
        0,
      ),
      'MIN' => array(
        6,
        -1,
        0,
        0,
      ),
      'MAX' => array(
        7,
        -1,
        0,
        0,
      ),
      'ROW' => array(
        8,
        -1,
        0,
        0,
      ),
      'COLUMN' => array(
        9,
        -1,
        0,
        0,
      ),
      'NA' => array(
        10,
        0,
        0,
        0,
      ),
      'NPV' => array(
        11,
        -1,
        1,
        0,
      ),
      'STDEV' => array(
        12,
        -1,
        0,
        0,
      ),
      'DOLLAR' => array(
        13,
        -1,
        1,
        0,
      ),
      'FIXED' => array(
        14,
        -1,
        1,
        0,
      ),
      'SIN' => array(
        15,
        1,
        1,
        0,
      ),
      'COS' => array(
        16,
        1,
        1,
        0,
      ),
      'TAN' => array(
        17,
        1,
        1,
        0,
      ),
      'ATAN' => array(
        18,
        1,
        1,
        0,
      ),
      'PI' => array(
        19,
        0,
        1,
        0,
      ),
      'SQRT' => array(
        20,
        1,
        1,
        0,
      ),
      'EXP' => array(
        21,
        1,
        1,
        0,
      ),
      'LN' => array(
        22,
        1,
        1,
        0,
      ),
      'LOG10' => array(
        23,
        1,
        1,
        0,
      ),
      'ABS' => array(
        24,
        1,
        1,
        0,
      ),
      'INT' => array(
        25,
        1,
        1,
        0,
      ),
      'SIGN' => array(
        26,
        1,
        1,
        0,
      ),
      'ROUND' => array(
        27,
        2,
        1,
        0,
      ),
      'LOOKUP' => array(
        28,
        -1,
        0,
        0,
      ),
      'INDEX' => array(
        29,
        -1,
        0,
        1,
      ),
      'REPT' => array(
        30,
        2,
        1,
        0,
      ),
      'MID' => array(
        31,
        3,
        1,
        0,
      ),
      'LEN' => array(
        32,
        1,
        1,
        0,
      ),
      'VALUE' => array(
        33,
        1,
        1,
        0,
      ),
      'TRUE' => array(
        34,
        0,
        1,
        0,
      ),
      'FALSE' => array(
        35,
        0,
        1,
        0,
      ),
      'AND' => array(
        36,
        -1,
        0,
        0,
      ),
      'OR' => array(
        37,
        -1,
        0,
        0,
      ),
      'NOT' => array(
        38,
        1,
        1,
        0,
      ),
      'MOD' => array(
        39,
        2,
        1,
        0,
      ),
      'DCOUNT' => array(
        40,
        3,
        0,
        0,
      ),
      'DSUM' => array(
        41,
        3,
        0,
        0,
      ),
      'DAVERAGE' => array(
        42,
        3,
        0,
        0,
      ),
      'DMIN' => array(
        43,
        3,
        0,
        0,
      ),
      'DMAX' => array(
        44,
        3,
        0,
        0,
      ),
      'DSTDEV' => array(
        45,
        3,
        0,
        0,
      ),
      'VAR' => array(
        46,
        -1,
        0,
        0,
      ),
      'DVAR' => array(
        47,
        3,
        0,
        0,
      ),
      'TEXT' => array(
        48,
        2,
        1,
        0,
      ),
      'LINEST' => array(
        49,
        -1,
        0,
        0,
      ),
      'TREND' => array(
        50,
        -1,
        0,
        0,
      ),
      'LOGEST' => array(
        51,
        -1,
        0,
        0,
      ),
      'GROWTH' => array(
        52,
        -1,
        0,
        0,
      ),
      'PV' => array(
        56,
        -1,
        1,
        0,
      ),
      'FV' => array(
        57,
        -1,
        1,
        0,
      ),
      'NPER' => array(
        58,
        -1,
        1,
        0,
      ),
      'PMT' => array(
        59,
        -1,
        1,
        0,
      ),
      'RATE' => array(
        60,
        -1,
        1,
        0,
      ),
      'MIRR' => array(
        61,
        3,
        0,
        0,
      ),
      'IRR' => array(
        62,
        -1,
        0,
        0,
      ),
      'RAND' => array(
        63,
        0,
        1,
        1,
      ),
      'MATCH' => array(
        64,
        -1,
        0,
        0,
      ),
      'DATE' => array(
        65,
        3,
        1,
        0,
      ),
      'TIME' => array(
        66,
        3,
        1,
        0,
      ),
      'DAY' => array(
        67,
        1,
        1,
        0,
      ),
      'MONTH' => array(
        68,
        1,
        1,
        0,
      ),
      'YEAR' => array(
        69,
        1,
        1,
        0,
      ),
      'WEEKDAY' => array(
        70,
        -1,
        1,
        0,
      ),
      'HOUR' => array(
        71,
        1,
        1,
        0,
      ),
      'MINUTE' => array(
        72,
        1,
        1,
        0,
      ),
      'SECOND' => array(
        73,
        1,
        1,
        0,
      ),
      'NOW' => array(
        74,
        0,
        1,
        1,
      ),
      'AREAS' => array(
        75,
        1,
        0,
        1,
      ),
      'ROWS' => array(
        76,
        1,
        0,
        1,
      ),
      'COLUMNS' => array(
        77,
        1,
        0,
        1,
      ),
      'OFFSET' => array(
        78,
        -1,
        0,
        1,
      ),
      'SEARCH' => array(
        82,
        -1,
        1,
        0,
      ),
      'TRANSPOSE' => array(
        83,
        1,
        1,
        0,
      ),
      'TYPE' => array(
        86,
        1,
        1,
        0,
      ),
      'ATAN2' => array(
        97,
        2,
        1,
        0,
      ),
      'ASIN' => array(
        98,
        1,
        1,
        0,
      ),
      'ACOS' => array(
        99,
        1,
        1,
        0,
      ),
      'CHOOSE' => array(
        100,
        -1,
        1,
        0,
      ),
      'HLOOKUP' => array(
        101,
        -1,
        0,
        0,
      ),
      'VLOOKUP' => array(
        102,
        -1,
        0,
        0,
      ),
      'ISREF' => array(
        105,
        1,
        0,
        0,
      ),
      'LOG' => array(
        109,
        -1,
        1,
        0,
      ),
      'CHAR' => array(
        111,
        1,
        1,
        0,
      ),
      'LOWER' => array(
        112,
        1,
        1,
        0,
      ),
      'UPPER' => array(
        113,
        1,
        1,
        0,
      ),
      'PROPER' => array(
        114,
        1,
        1,
        0,
      ),
      'LEFT' => array(
        115,
        -1,
        1,
        0,
      ),
      'RIGHT' => array(
        116,
        -1,
        1,
        0,
      ),
      'EXACT' => array(
        117,
        2,
        1,
        0,
      ),
      'TRIM' => array(
        118,
        1,
        1,
        0,
      ),
      'REPLACE' => array(
        119,
        4,
        1,
        0,
      ),
      'SUBSTITUTE' => array(
        120,
        -1,
        1,
        0,
      ),
      'CODE' => array(
        121,
        1,
        1,
        0,
      ),
      'FIND' => array(
        124,
        -1,
        1,
        0,
      ),
      'CELL' => array(
        125,
        -1,
        0,
        1,
      ),
      'ISERR' => array(
        126,
        1,
        1,
        0,
      ),
      'ISTEXT' => array(
        127,
        1,
        1,
        0,
      ),
      'ISNUMBER' => array(
        128,
        1,
        1,
        0,
      ),
      'ISBLANK' => array(
        129,
        1,
        1,
        0,
      ),
      'T' => array(
        130,
        1,
        0,
        0,
      ),
      'N' => array(
        131,
        1,
        0,
        0,
      ),
      'DATEVALUE' => array(
        140,
        1,
        1,
        0,
      ),
      'TIMEVALUE' => array(
        141,
        1,
        1,
        0,
      ),
      'SLN' => array(
        142,
        3,
        1,
        0,
      ),
      'SYD' => array(
        143,
        4,
        1,
        0,
      ),
      'DDB' => array(
        144,
        -1,
        1,
        0,
      ),
      'INDIRECT' => array(
        148,
        -1,
        1,
        1,
      ),
      'CALL' => array(
        150,
        -1,
        1,
        0,
      ),
      'CLEAN' => array(
        162,
        1,
        1,
        0,
      ),
      'MDETERM' => array(
        163,
        1,
        2,
        0,
      ),
      'MINVERSE' => array(
        164,
        1,
        2,
        0,
      ),
      'MMULT' => array(
        165,
        2,
        2,
        0,
      ),
      'IPMT' => array(
        167,
        -1,
        1,
        0,
      ),
      'PPMT' => array(
        168,
        -1,
        1,
        0,
      ),
      'COUNTA' => array(
        169,
        -1,
        0,
        0,
      ),
      'PRODUCT' => array(
        183,
        -1,
        0,
        0,
      ),
      'FACT' => array(
        184,
        1,
        1,
        0,
      ),
      'DPRODUCT' => array(
        189,
        3,
        0,
        0,
      ),
      'ISNONTEXT' => array(
        190,
        1,
        1,
        0,
      ),
      'STDEVP' => array(
        193,
        -1,
        0,
        0,
      ),
      'VARP' => array(
        194,
        -1,
        0,
        0,
      ),
      'DSTDEVP' => array(
        195,
        3,
        0,
        0,
      ),
      'DVARP' => array(
        196,
        3,
        0,
        0,
      ),
      'TRUNC' => array(
        197,
        -1,
        1,
        0,
      ),
      'ISLOGICAL' => array(
        198,
        1,
        1,
        0,
      ),
      'DCOUNTA' => array(
        199,
        3,
        0,
        0,
      ),
      'USDOLLAR' => array(
        204,
        -1,
        1,
        0,
      ),
      'FINDB' => array(
        205,
        -1,
        1,
        0,
      ),
      'SEARCHB' => array(
        206,
        -1,
        1,
        0,
      ),
      'REPLACEB' => array(
        207,
        4,
        1,
        0,
      ),
      'LEFTB' => array(
        208,
        -1,
        1,
        0,
      ),
      'RIGHTB' => array(
        209,
        -1,
        1,
        0,
      ),
      'MIDB' => array(
        210,
        3,
        1,
        0,
      ),
      'LENB' => array(
        211,
        1,
        1,
        0,
      ),
      'ROUNDUP' => array(
        212,
        2,
        1,
        0,
      ),
      'ROUNDDOWN' => array(
        213,
        2,
        1,
        0,
      ),
      'ASC' => array(
        214,
        1,
        1,
        0,
      ),
      'DBCS' => array(
        215,
        1,
        1,
        0,
      ),
      'RANK' => array(
        216,
        -1,
        0,
        0,
      ),
      'ADDRESS' => array(
        219,
        -1,
        1,
        0,
      ),
      'DAYS360' => array(
        220,
        -1,
        1,
        0,
      ),
      'TODAY' => array(
        221,
        0,
        1,
        1,
      ),
      'VDB' => array(
        222,
        -1,
        1,
        0,
      ),
      'MEDIAN' => array(
        227,
        -1,
        0,
        0,
      ),
      'SUMPRODUCT' => array(
        228,
        -1,
        2,
        0,
      ),
      'SINH' => array(
        229,
        1,
        1,
        0,
      ),
      'COSH' => array(
        230,
        1,
        1,
        0,
      ),
      'TANH' => array(
        231,
        1,
        1,
        0,
      ),
      'ASINH' => array(
        232,
        1,
        1,
        0,
      ),
      'ACOSH' => array(
        233,
        1,
        1,
        0,
      ),
      'ATANH' => array(
        234,
        1,
        1,
        0,
      ),
      'DGET' => array(
        235,
        3,
        0,
        0,
      ),
      'INFO' => array(
        244,
        1,
        1,
        1,
      ),
      'DB' => array(
        247,
        -1,
        1,
        0,
      ),
      'FREQUENCY' => array(
        252,
        2,
        0,
        0,
      ),
      'ERROR.TYPE' => array(
        261,
        1,
        1,
        0,
      ),
      'REGISTER.ID' => array(
        267,
        -1,
        1,
        0,
      ),
      'AVEDEV' => array(
        269,
        -1,
        0,
        0,
      ),
      'BETADIST' => array(
        270,
        -1,
        1,
        0,
      ),
      'GAMMALN' => array(
        271,
        1,
        1,
        0,
      ),
      'BETAINV' => array(
        272,
        -1,
        1,
        0,
      ),
      'BINOMDIST' => array(
        273,
        4,
        1,
        0,
      ),
      'CHIDIST' => array(
        274,
        2,
        1,
        0,
      ),
      'CHIINV' => array(
        275,
        2,
        1,
        0,
      ),
      'COMBIN' => array(
        276,
        2,
        1,
        0,
      ),
      'CONFIDENCE' => array(
        277,
        3,
        1,
        0,
      ),
      'CRITBINOM' => array(
        278,
        3,
        1,
        0,
      ),
      'EVEN' => array(
        279,
        1,
        1,
        0,
      ),
      'EXPONDIST' => array(
        280,
        3,
        1,
        0,
      ),
      'FDIST' => array(
        281,
        3,
        1,
        0,
      ),
      'FINV' => array(
        282,
        3,
        1,
        0,
      ),
      'FISHER' => array(
        283,
        1,
        1,
        0,
      ),
      'FISHERINV' => array(
        284,
        1,
        1,
        0,
      ),
      'FLOOR' => array(
        285,
        2,
        1,
        0,
      ),
      'GAMMADIST' => array(
        286,
        4,
        1,
        0,
      ),
      'GAMMAINV' => array(
        287,
        3,
        1,
        0,
      ),
      'CEILING' => array(
        288,
        2,
        1,
        0,
      ),
      'HYPGEOMDIST' => array(
        289,
        4,
        1,
        0,
      ),
      'LOGNORMDIST' => array(
        290,
        3,
        1,
        0,
      ),
      'LOGINV' => array(
        291,
        3,
        1,
        0,
      ),
      'NEGBINOMDIST' => array(
        292,
        3,
        1,
        0,
      ),
      'NORMDIST' => array(
        293,
        4,
        1,
        0,
      ),
      'NORMSDIST' => array(
        294,
        1,
        1,
        0,
      ),
      'NORMINV' => array(
        295,
        3,
        1,
        0,
      ),
      'NORMSINV' => array(
        296,
        1,
        1,
        0,
      ),
      'STANDARDIZE' => array(
        297,
        3,
        1,
        0,
      ),
      'ODD' => array(
        298,
        1,
        1,
        0,
      ),
      'PERMUT' => array(
        299,
        2,
        1,
        0,
      ),
      'POISSON' => array(
        300,
        3,
        1,
        0,
      ),
      'TDIST' => array(
        301,
        3,
        1,
        0,
      ),
      'WEIBULL' => array(
        302,
        4,
        1,
        0,
      ),
      'SUMXMY2' => array(
        303,
        2,
        2,
        0,
      ),
      'SUMX2MY2' => array(
        304,
        2,
        2,
        0,
      ),
      'SUMX2PY2' => array(
        305,
        2,
        2,
        0,
      ),
      'CHITEST' => array(
        306,
        2,
        2,
        0,
      ),
      'CORREL' => array(
        307,
        2,
        2,
        0,
      ),
      'COVAR' => array(
        308,
        2,
        2,
        0,
      ),
      'FORECAST' => array(
        309,
        3,
        2,
        0,
      ),
      'FTEST' => array(
        310,
        2,
        2,
        0,
      ),
      'INTERCEPT' => array(
        311,
        2,
        2,
        0,
      ),
      'PEARSON' => array(
        312,
        2,
        2,
        0,
      ),
      'RSQ' => array(
        313,
        2,
        2,
        0,
      ),
      'STEYX' => array(
        314,
        2,
        2,
        0,
      ),
      'SLOPE' => array(
        315,
        2,
        2,
        0,
      ),
      'TTEST' => array(
        316,
        4,
        2,
        0,
      ),
      'PROB' => array(
        317,
        -1,
        2,
        0,
      ),
      'DEVSQ' => array(
        318,
        -1,
        0,
        0,
      ),
      'GEOMEAN' => array(
        319,
        -1,
        0,
        0,
      ),
      'HARMEAN' => array(
        320,
        -1,
        0,
        0,
      ),
      'SUMSQ' => array(
        321,
        -1,
        0,
        0,
      ),
      'KURT' => array(
        322,
        -1,
        0,
        0,
      ),
      'SKEW' => array(
        323,
        -1,
        0,
        0,
      ),
      'ZTEST' => array(
        324,
        -1,
        0,
        0,
      ),
      'LARGE' => array(
        325,
        2,
        0,
        0,
      ),
      'SMALL' => array(
        326,
        2,
        0,
        0,
      ),
      'QUARTILE' => array(
        327,
        2,
        0,
        0,
      ),
      'PERCENTILE' => array(
        328,
        2,
        0,
        0,
      ),
      'PERCENTRANK' => array(
        329,
        -1,
        0,
        0,
      ),
      'MODE' => array(
        330,
        -1,
        2,
        0,
      ),
      'TRIMMEAN' => array(
        331,
        2,
        0,
        0,
      ),
      'TINV' => array(
        332,
        2,
        1,
        0,
      ),
      'CONCATENATE' => array(
        336,
        -1,
        1,
        0,
      ),
      'POWER' => array(
        337,
        2,
        1,
        0,
      ),
      'RADIANS' => array(
        342,
        1,
        1,
        0,
      ),
      'DEGREES' => array(
        343,
        1,
        1,
        0,
      ),
      'SUBTOTAL' => array(
        344,
        -1,
        0,
        0,
      ),
      'SUMIF' => array(
        345,
        -1,
        0,
        0,
      ),
      'COUNTIF' => array(
        346,
        2,
        0,
        0,
      ),
      'COUNTBLANK' => array(
        347,
        1,
        0,
        0,
      ),
      'ISPMT' => array(
        350,
        4,
        1,
        0,
      ),
      'DATEDIF' => array(
        351,
        3,
        1,
        0,
      ),
      'DATESTRING' => array(
        352,
        1,
        1,
        0,
      ),
      'NUMBERSTRING' => array(
        353,
        2,
        1,
        0,
      ),
      'ROMAN' => array(
        354,
        -1,
        1,
        0,
      ),
      'GETPIVOTDATA' => array(
        358,
        -1,
        0,
        0,
      ),
      'HYPERLINK' => array(
        359,
        -1,
        1,
        0,
      ),
      'PHONETIC' => array(
        360,
        1,
        0,
        0,
      ),
      'AVERAGEA' => array(
        361,
        -1,
        0,
        0,
      ),
      'MAXA' => array(
        362,
        -1,
        0,
        0,
      ),
      'MINA' => array(
        363,
        -1,
        0,
        0,
      ),
      'STDEVPA' => array(
        364,
        -1,
        0,
        0,
      ),
      'VARPA' => array(
        365,
        -1,
        0,
        0,
      ),
      'STDEVA' => array(
        366,
        -1,
        0,
        0,
      ),
      'VARA' => array(
        367,
        -1,
        0,
        0,
      ),
      'BAHTTEXT' => array(
        368,
        1,
        0,
        0,
      ),
    );
  }

  /**
   * Convert a token to the proper ptg value.
   *
   * @access private
   * @param mixed $token The token to convert.
   * @return mixed the converted token on success
   */
  function _convert($token) {
    if (preg_match("/\"([^\"]|\"\"){0,255}\"/", $token)) {
      return $this
        ->_convertString($token);
    }
    elseif (is_numeric($token)) {
      return $this
        ->_convertNumber($token);

      // match references like A1 or $A$1
    }
    elseif (preg_match('/^\\$?([A-Ia-i]?[A-Za-z])\\$?(\\d+)$/', $token)) {
      return $this
        ->_convertRef2d($token);

      // match external references like Sheet1!A1 or Sheet1:Sheet2!A1 or Sheet1!$A$1 or Sheet1:Sheet2!$A$1
    }
    elseif (preg_match("/^" . self::REGEX_SHEET_TITLE_UNQUOTED . "(\\:" . self::REGEX_SHEET_TITLE_UNQUOTED . ")?\\!\\\$?[A-Ia-i]?[A-Za-z]\\\$?(\\d+)\$/u", $token)) {
      return $this
        ->_convertRef3d($token);

      // match external references like 'Sheet1'!A1 or 'Sheet1:Sheet2'!A1 or 'Sheet1'!$A$1 or 'Sheet1:Sheet2'!$A$1
    }
    elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . "(\\:" . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\\$?[A-Ia-i]?[A-Za-z]\\\$?(\\d+)\$/u", $token)) {
      return $this
        ->_convertRef3d($token);

      // match ranges like A1:B2 or $A$1:$B$2
    }
    elseif (preg_match('/^(\\$)?[A-Ia-i]?[A-Za-z](\\$)?(\\d+)\\:(\\$)?[A-Ia-i]?[A-Za-z](\\$)?(\\d+)$/', $token)) {
      return $this
        ->_convertRange2d($token);

      // match external ranges like Sheet1!A1:B2 or Sheet1:Sheet2!A1:B2 or Sheet1!$A$1:$B$2 or Sheet1:Sheet2!$A$1:$B$2
    }
    elseif (preg_match("/^" . self::REGEX_SHEET_TITLE_UNQUOTED . "(\\:" . self::REGEX_SHEET_TITLE_UNQUOTED . ")?\\!\\\$?([A-Ia-i]?[A-Za-z])?\\\$?(\\d+)\\:\\\$?([A-Ia-i]?[A-Za-z])?\\\$?(\\d+)\$/u", $token)) {
      return $this
        ->_convertRange3d($token);

      // match external ranges like 'Sheet1'!A1:B2 or 'Sheet1:Sheet2'!A1:B2 or 'Sheet1'!$A$1:$B$2 or 'Sheet1:Sheet2'!$A$1:$B$2
    }
    elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . "(\\:" . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\\$?([A-Ia-i]?[A-Za-z])?\\\$?(\\d+)\\:\\\$?([A-Ia-i]?[A-Za-z])?\\\$?(\\d+)\$/u", $token)) {
      return $this
        ->_convertRange3d($token);

      // operators (including parentheses)
    }
    elseif (isset($this->ptg[$token])) {
      return pack("C", $this->ptg[$token]);

      // match error codes
    }
    elseif (preg_match("/^#[A-Z0\\/]{3,5}[!?]{1}\$/", $token) or $token == '#N/A') {
      return $this
        ->_convertError($token);

      // commented so argument number can be processed correctly. See toReversePolish().

      /*elseif (preg_match("/[A-Z0-9\xc0-\xdc\.]+/",$token))
      		{
      			return($this->_convertFunction($token,$this->_func_args));
      		}*/

      // if it's an argument, ignore the token (the argument remains)
    }
    elseif ($token == 'arg') {
      return '';
    }

    // TODO: use real error codes
    throw new PHPExcel_Writer_Exception("Unknown token {$token}");
  }

  /**
   * Convert a number token to ptgInt or ptgNum
   *
   * @access private
   * @param mixed $num an integer or double for conversion to its ptg value
   */
  function _convertNumber($num) {

    // Integer in the range 0..2**16-1
    if (preg_match("/^\\d+\$/", $num) and $num <= 65535) {
      return pack("Cv", $this->ptg['ptgInt'], $num);
    }
    else {

      // A float
      if (PHPExcel_Writer_Excel5_BIFFwriter::getByteOrder()) {

        // if it's Big Endian
        $num = strrev($num);
      }
      return pack("Cd", $this->ptg['ptgNum'], $num);
    }
  }

  /**
   * Convert a string token to ptgStr
   *
   * @access private
   * @param string $string A string for conversion to its ptg value.
   * @return mixed the converted token on success
   */
  function _convertString($string) {

    // chop away beggining and ending quotes
    $string = substr($string, 1, strlen($string) - 2);
    if (strlen($string) > 255) {
      throw new PHPExcel_Writer_Exception("String is too long");
    }
    return pack('C', $this->ptg['ptgStr']) . PHPExcel_Shared_String::UTF8toBIFF8UnicodeShort($string);
  }

  /**
   * Convert a function to a ptgFunc or ptgFuncVarV depending on the number of
   * args that it takes.
   *
   * @access private
   * @param string  $token    The name of the function for convertion to ptg value.
   * @param integer $num_args The number of arguments the function receives.
   * @return string The packed ptg for the function
   */
  function _convertFunction($token, $num_args) {
    $args = $this->_functions[$token][1];

    //		$volatile = $this->_functions[$token][3];
    // Fixed number of args eg. TIME($i,$j,$k).
    if ($args >= 0) {
      return pack("Cv", $this->ptg['ptgFuncV'], $this->_functions[$token][0]);
    }

    // Variable number of args eg. SUM($i,$j,$k, ..).
    if ($args == -1) {
      return pack("CCv", $this->ptg['ptgFuncVarV'], $num_args, $this->_functions[$token][0]);
    }
  }

  /**
   * Convert an Excel range such as A1:D4 to a ptgRefV.
   *
   * @access private
   * @param string	$range	An Excel range in the A1:A2
   * @param int		$class
   */
  function _convertRange2d($range, $class = 0) {

    // TODO: possible class value 0,1,2 check Formula.pm
    // Split the range into 2 cell refs
    if (preg_match('/^(\\$)?([A-Ia-i]?[A-Za-z])(\\$)?(\\d+)\\:(\\$)?([A-Ia-i]?[A-Za-z])(\\$)?(\\d+)$/', $range)) {
      list($cell1, $cell2) = explode(':', $range);
    }
    else {

      // TODO: use real error codes
      throw new PHPExcel_Writer_Exception("Unknown range separator");
    }

    // Convert the cell references
    list($row1, $col1) = $this
      ->_cellToPackedRowcol($cell1);
    list($row2, $col2) = $this
      ->_cellToPackedRowcol($cell2);

    // The ptg value depends on the class of the ptg.
    if ($class == 0) {
      $ptgArea = pack("C", $this->ptg['ptgArea']);
    }
    elseif ($class == 1) {
      $ptgArea = pack("C", $this->ptg['ptgAreaV']);
    }
    elseif ($class == 2) {
      $ptgArea = pack("C", $this->ptg['ptgAreaA']);
    }
    else {

      // TODO: use real error codes
      throw new PHPExcel_Writer_Exception("Unknown class {$class}");
    }
    return $ptgArea . $row1 . $row2 . $col1 . $col2;
  }

  /**
   * Convert an Excel 3d range such as "Sheet1!A1:D4" or "Sheet1:Sheet2!A1:D4" to
   * a ptgArea3d.
   *
   * @access private
   * @param string $token An Excel range in the Sheet1!A1:A2 format.
   * @return mixed The packed ptgArea3d token on success.
   */
  function _convertRange3d($token) {

    //		$class = 0; // formulas like Sheet1!$A$1:$A$2 in list type data validation need this class (0x3B)
    // Split the ref at the ! symbol
    list($ext_ref, $range) = explode('!', $token);

    // Convert the external reference part (different for BIFF8)
    $ext_ref = $this
      ->_getRefIndex($ext_ref);

    // Split the range into 2 cell refs
    list($cell1, $cell2) = explode(':', $range);

    // Convert the cell references
    if (preg_match("/^(\\\$)?[A-Ia-i]?[A-Za-z](\\\$)?(\\d+)\$/", $cell1)) {
      list($row1, $col1) = $this
        ->_cellToPackedRowcol($cell1);
      list($row2, $col2) = $this
        ->_cellToPackedRowcol($cell2);
    }
    else {

      // It's a rows range (like 26:27)
      list($row1, $col1, $row2, $col2) = $this
        ->_rangeToPackedRange($cell1 . ':' . $cell2);
    }

    // The ptg value depends on the class of the ptg.
    //		if ($class == 0) {
    $ptgArea = pack("C", $this->ptg['ptgArea3d']);

    //		} elseif ($class == 1) {
    //			$ptgArea = pack("C", $this->ptg['ptgArea3dV']);
    //		} elseif ($class == 2) {
    //			$ptgArea = pack("C", $this->ptg['ptgArea3dA']);
    //		} else {
    //			throw new PHPExcel_Writer_Exception("Unknown class $class");
    //		}
    return $ptgArea . $ext_ref . $row1 . $row2 . $col1 . $col2;
  }

  /**
   * Convert an Excel reference such as A1, $B2, C$3 or $D$4 to a ptgRefV.
   *
   * @access private
   * @param string $cell An Excel cell reference
   * @return string The cell in packed() format with the corresponding ptg
   */
  function _convertRef2d($cell) {

    //		$class = 2; // as far as I know, this is magick.
    // Convert the cell reference
    $cell_array = $this
      ->_cellToPackedRowcol($cell);
    list($row, $col) = $cell_array;

    // The ptg value depends on the class of the ptg.
    //		if ($class == 0) {
    //			$ptgRef = pack("C", $this->ptg['ptgRef']);
    //		} elseif ($class == 1) {
    //			$ptgRef = pack("C", $this->ptg['ptgRefV']);
    //		} elseif ($class == 2) {
    $ptgRef = pack("C", $this->ptg['ptgRefA']);

    //		} else {
    //			// TODO: use real error codes
    //			throw new PHPExcel_Writer_Exception("Unknown class $class");
    //		}
    return $ptgRef . $row . $col;
  }

  /**
   * Convert an Excel 3d reference such as "Sheet1!A1" or "Sheet1:Sheet2!A1" to a
   * ptgRef3d.
   *
   * @access private
   * @param string $cell An Excel cell reference
   * @return mixed The packed ptgRef3d token on success.
   */
  function _convertRef3d($cell) {

    //		$class = 2; // as far as I know, this is magick.
    // Split the ref at the ! symbol
    list($ext_ref, $cell) = explode('!', $cell);

    // Convert the external reference part (different for BIFF8)
    $ext_ref = $this
      ->_getRefIndex($ext_ref);

    // Convert the cell reference part
    list($row, $col) = $this
      ->_cellToPackedRowcol($cell);

    // The ptg value depends on the class of the ptg.
    //		if ($class == 0) {
    //			$ptgRef = pack("C", $this->ptg['ptgRef3d']);
    //		} elseif ($class == 1) {
    //			$ptgRef = pack("C", $this->ptg['ptgRef3dV']);
    //		} elseif ($class == 2) {
    $ptgRef = pack("C", $this->ptg['ptgRef3dA']);

    //		} else {
    //			throw new PHPExcel_Writer_Exception("Unknown class $class");
    //		}
    return $ptgRef . $ext_ref . $row . $col;
  }

  /**
   * Convert an error code to a ptgErr
   *
   * @access	private
   * @param	string	$errorCode	The error code for conversion to its ptg value
   * @return	string				The error code ptgErr
   */
  function _convertError($errorCode) {
    switch ($errorCode) {
      case '#NULL!':
        return pack("C", 0x0);
      case '#DIV/0!':
        return pack("C", 0x7);
      case '#VALUE!':
        return pack("C", 0xf);
      case '#REF!':
        return pack("C", 0x17);
      case '#NAME?':
        return pack("C", 0x1d);
      case '#NUM!':
        return pack("C", 0x24);
      case '#N/A':
        return pack("C", 0x2a);
    }
    return pack("C", 0xff);
  }

  /**
   * Convert the sheet name part of an external reference, for example "Sheet1" or
   * "Sheet1:Sheet2", to a packed structure.
   *
   * @access	private
   * @param	string	$ext_ref	The name of the external reference
   * @return	string				The reference index in packed() format
   */
  function _packExtRef($ext_ref) {
    $ext_ref = preg_replace("/^'/", '', $ext_ref);

    // Remove leading  ' if any.
    $ext_ref = preg_replace("/'\$/", '', $ext_ref);

    // Remove trailing ' if any.
    // Check if there is a sheet range eg., Sheet1:Sheet2.
    if (preg_match("/:/", $ext_ref)) {
      list($sheet_name1, $sheet_name2) = explode(':', $ext_ref);
      $sheet1 = $this
        ->_getSheetIndex($sheet_name1);
      if ($sheet1 == -1) {
        throw new PHPExcel_Writer_Exception("Unknown sheet name {$sheet_name1} in formula");
      }
      $sheet2 = $this
        ->_getSheetIndex($sheet_name2);
      if ($sheet2 == -1) {
        throw new PHPExcel_Writer_Exception("Unknown sheet name {$sheet_name2} in formula");
      }

      // Reverse max and min sheet numbers if necessary
      if ($sheet1 > $sheet2) {
        list($sheet1, $sheet2) = array(
          $sheet2,
          $sheet1,
        );
      }
    }
    else {

      // Single sheet name only.
      $sheet1 = $this
        ->_getSheetIndex($ext_ref);
      if ($sheet1 == -1) {
        throw new PHPExcel_Writer_Exception("Unknown sheet name {$ext_ref} in formula");
      }
      $sheet2 = $sheet1;
    }

    // References are stored relative to 0xFFFF.
    $offset = -1 - $sheet1;
    return pack('vdvv', $offset, 0x0, $sheet1, $sheet2);
  }

  /**
   * Look up the REF index that corresponds to an external sheet name
   * (or range). If it doesn't exist yet add it to the workbook's references
   * array. It assumes all sheet names given must exist.
   *
   * @access private
   * @param string $ext_ref The name of the external reference
   * @return mixed The reference index in packed() format on success
   */
  function _getRefIndex($ext_ref) {
    $ext_ref = preg_replace("/^'/", '', $ext_ref);

    // Remove leading  ' if any.
    $ext_ref = preg_replace("/'\$/", '', $ext_ref);

    // Remove trailing ' if any.
    $ext_ref = str_replace('\'\'', '\'', $ext_ref);

    // Replace escaped '' with '
    // Check if there is a sheet range eg., Sheet1:Sheet2.
    if (preg_match("/:/", $ext_ref)) {
      list($sheet_name1, $sheet_name2) = explode(':', $ext_ref);
      $sheet1 = $this
        ->_getSheetIndex($sheet_name1);
      if ($sheet1 == -1) {
        throw new PHPExcel_Writer_Exception("Unknown sheet name {$sheet_name1} in formula");
      }
      $sheet2 = $this
        ->_getSheetIndex($sheet_name2);
      if ($sheet2 == -1) {
        throw new PHPExcel_Writer_Exception("Unknown sheet name {$sheet_name2} in formula");
      }

      // Reverse max and min sheet numbers if necessary
      if ($sheet1 > $sheet2) {
        list($sheet1, $sheet2) = array(
          $sheet2,
          $sheet1,
        );
      }
    }
    else {

      // Single sheet name only.
      $sheet1 = $this
        ->_getSheetIndex($ext_ref);
      if ($sheet1 == -1) {
        throw new PHPExcel_Writer_Exception("Unknown sheet name {$ext_ref} in formula");
      }
      $sheet2 = $sheet1;
    }

    // assume all references belong to this document
    $supbook_index = 0x0;
    $ref = pack('vvv', $supbook_index, $sheet1, $sheet2);
    $total_references = count($this->_references);
    $index = -1;
    for ($i = 0; $i < $total_references; ++$i) {
      if ($ref == $this->_references[$i]) {
        $index = $i;
        break;
      }
    }

    // if REF was not found add it to references array
    if ($index == -1) {
      $this->_references[$total_references] = $ref;
      $index = $total_references;
    }
    return pack('v', $index);
  }

  /**
   * Look up the index that corresponds to an external sheet name. The hash of
   * sheet names is updated by the addworksheet() method of the
   * PHPExcel_Writer_Excel5_Workbook class.
   *
   * @access	private
   * @param	string	$sheet_name		Sheet name
   * @return	integer					The sheet index, -1 if the sheet was not found
   */
  function _getSheetIndex($sheet_name) {
    if (!isset($this->_ext_sheets[$sheet_name])) {
      return -1;
    }
    else {
      return $this->_ext_sheets[$sheet_name];
    }
  }

  /**
   * This method is used to update the array of sheet names. It is
   * called by the addWorksheet() method of the
   * PHPExcel_Writer_Excel5_Workbook class.
   *
   * @access public
   * @see PHPExcel_Writer_Excel5_Workbook::addWorksheet()
   * @param string  $name  The name of the worksheet being added
   * @param integer $index The index of the worksheet being added
   */
  function setExtSheet($name, $index) {
    $this->_ext_sheets[$name] = $index;
  }

  /**
   * pack() row and column into the required 3 or 4 byte format.
   *
   * @access private
   * @param string $cell The Excel cell reference to be packed
   * @return array Array containing the row and column in packed() format
   */
  function _cellToPackedRowcol($cell) {
    $cell = strtoupper($cell);
    list($row, $col, $row_rel, $col_rel) = $this
      ->_cellToRowcol($cell);
    if ($col >= 256) {
      throw new PHPExcel_Writer_Exception("Column in: {$cell} greater than 255");
    }
    if ($row >= 65536) {
      throw new PHPExcel_Writer_Exception("Row in: {$cell} greater than 65536 ");
    }

    // Set the high bits to indicate if row or col are relative.
    $col |= $col_rel << 14;
    $col |= $row_rel << 15;
    $col = pack('v', $col);
    $row = pack('v', $row);
    return array(
      $row,
      $col,
    );
  }

  /**
   * pack() row range into the required 3 or 4 byte format.
   * Just using maximum col/rows, which is probably not the correct solution
   *
   * @access private
   * @param string $range The Excel range to be packed
   * @return array Array containing (row1,col1,row2,col2) in packed() format
   */
  function _rangeToPackedRange($range) {
    preg_match('/(\\$)?(\\d+)\\:(\\$)?(\\d+)/', $range, $match);

    // return absolute rows if there is a $ in the ref
    $row1_rel = empty($match[1]) ? 1 : 0;
    $row1 = $match[2];
    $row2_rel = empty($match[3]) ? 1 : 0;
    $row2 = $match[4];

    // Convert 1-index to zero-index
    --$row1;
    --$row2;

    // Trick poor inocent Excel
    $col1 = 0;
    $col2 = 65535;

    // FIXME: maximum possible value for Excel 5 (change this!!!)
    // FIXME: this changes for BIFF8
    if ($row1 >= 65536 or $row2 >= 65536) {
      throw new PHPExcel_Writer_Exception("Row in: {$range} greater than 65536 ");
    }

    // Set the high bits to indicate if rows are relative.
    $col1 |= $row1_rel << 15;
    $col2 |= $row2_rel << 15;
    $col1 = pack('v', $col1);
    $col2 = pack('v', $col2);
    $row1 = pack('v', $row1);
    $row2 = pack('v', $row2);
    return array(
      $row1,
      $col1,
      $row2,
      $col2,
    );
  }

  /**
   * Convert an Excel cell reference such as A1 or $B2 or C$3 or $D$4 to a zero
   * indexed row and column number. Also returns two (0,1) values to indicate
   * whether the row or column are relative references.
   *
   * @access private
   * @param string $cell The Excel cell reference in A1 format.
   * @return array
   */
  function _cellToRowcol($cell) {
    preg_match('/(\\$)?([A-I]?[A-Z])(\\$)?(\\d+)/', $cell, $match);

    // return absolute column if there is a $ in the ref
    $col_rel = empty($match[1]) ? 1 : 0;
    $col_ref = $match[2];
    $row_rel = empty($match[3]) ? 1 : 0;
    $row = $match[4];

    // Convert base26 column string to a number.
    $expn = strlen($col_ref) - 1;
    $col = 0;
    $col_ref_length = strlen($col_ref);
    for ($i = 0; $i < $col_ref_length; ++$i) {
      $col += (ord($col_ref[$i]) - 64) * pow(26, $expn);
      --$expn;
    }

    // Convert 1-index to zero-index
    --$row;
    --$col;
    return array(
      $row,
      $col,
      $row_rel,
      $col_rel,
    );
  }

  /**
   * Advance to the next valid token.
   *
   * @access private
   */
  function _advance() {
    $i = $this->_current_char;
    $formula_length = strlen($this->_formula);

    // eat up white spaces
    if ($i < $formula_length) {
      while ($this->_formula[$i] == " ") {
        ++$i;
      }
      if ($i < $formula_length - 1) {
        $this->_lookahead = $this->_formula[$i + 1];
      }
      $token = '';
    }
    while ($i < $formula_length) {
      $token .= $this->_formula[$i];
      if ($i < $formula_length - 1) {
        $this->_lookahead = $this->_formula[$i + 1];
      }
      else {
        $this->_lookahead = '';
      }
      if ($this
        ->_match($token) != '') {

        //if ($i < strlen($this->_formula) - 1) {

        //    $this->_lookahead = $this->_formula{$i+1};

        //}
        $this->_current_char = $i + 1;
        $this->_current_token = $token;
        return 1;
      }
      if ($i < $formula_length - 2) {
        $this->_lookahead = $this->_formula[$i + 2];
      }
      else {

        // if we run out of characters _lookahead becomes empty
        $this->_lookahead = '';
      }
      ++$i;
    }

    //die("Lexical error ".$this->_current_char);
  }

  /**
   * Checks if it's a valid token.
   *
   * @access private
   * @param mixed $token The token to check.
   * @return mixed       The checked token or false on failure
   */
  function _match($token) {
    switch ($token) {
      case "+":
      case "-":
      case "*":
      case "/":
      case "(":
      case ")":
      case ",":
      case ";":
      case ">=":
      case "<=":
      case "=":
      case "<>":
      case "^":
      case "&":
      case "%":
        return $token;
        break;
      case ">":
        if ($this->_lookahead == '=') {

          // it's a GE token
          break;
        }
        return $token;
        break;
      case "<":

        // it's a LE or a NE token
        if ($this->_lookahead == '=' or $this->_lookahead == '>') {
          break;
        }
        return $token;
        break;
      default:

        // if it's a reference A1 or $A$1 or $A1 or A$1
        if (preg_match('/^\\$?[A-Ia-i]?[A-Za-z]\\$?[0-9]+$/', $token) and !preg_match("/[0-9]/", $this->_lookahead) and $this->_lookahead != ':' and $this->_lookahead != '.' and $this->_lookahead != '!') {
          return $token;
        }
        elseif (preg_match("/^" . self::REGEX_SHEET_TITLE_UNQUOTED . "(\\:" . self::REGEX_SHEET_TITLE_UNQUOTED . ")?\\!\\\$?[A-Ia-i]?[A-Za-z]\\\$?[0-9]+\$/u", $token) and !preg_match("/[0-9]/", $this->_lookahead) and $this->_lookahead != ':' and $this->_lookahead != '.') {
          return $token;
        }
        elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . "(\\:" . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\\$?[A-Ia-i]?[A-Za-z]\\\$?[0-9]+\$/u", $token) and !preg_match("/[0-9]/", $this->_lookahead) and $this->_lookahead != ':' and $this->_lookahead != '.') {
          return $token;
        }
        elseif (preg_match('/^(\\$)?[A-Ia-i]?[A-Za-z](\\$)?[0-9]+:(\\$)?[A-Ia-i]?[A-Za-z](\\$)?[0-9]+$/', $token) and !preg_match("/[0-9]/", $this->_lookahead)) {
          return $token;
        }
        elseif (preg_match("/^" . self::REGEX_SHEET_TITLE_UNQUOTED . "(\\:" . self::REGEX_SHEET_TITLE_UNQUOTED . ")?\\!\\\$?([A-Ia-i]?[A-Za-z])?\\\$?[0-9]+:\\\$?([A-Ia-i]?[A-Za-z])?\\\$?[0-9]+\$/u", $token) and !preg_match("/[0-9]/", $this->_lookahead)) {
          return $token;
        }
        elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . "(\\:" . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\\$?([A-Ia-i]?[A-Za-z])?\\\$?[0-9]+:\\\$?([A-Ia-i]?[A-Za-z])?\\\$?[0-9]+\$/u", $token) and !preg_match("/[0-9]/", $this->_lookahead)) {
          return $token;
        }
        elseif (is_numeric($token) and (!is_numeric($token . $this->_lookahead) or $this->_lookahead == '') and $this->_lookahead != '!' and $this->_lookahead != ':') {
          return $token;
        }
        elseif (preg_match("/\"([^\"]|\"\"){0,255}\"/", $token) and $this->_lookahead != '"' and substr_count($token, '"') % 2 == 0) {
          return $token;
        }
        elseif (preg_match("/^#[A-Z0\\/]{3,5}[!?]{1}\$/", $token) or $token == '#N/A') {
          return $token;
        }
        elseif (preg_match("", $token) and $this->_lookahead == "(") {
          return $token;
        }
        elseif (substr($token, -1) == ')') {
          return $token;
        }
        return '';
    }
  }

  /**
   * The parsing method. It parses a formula.
   *
   * @access public
   * @param string $formula The formula to parse, without the initial equal
   *                        sign (=).
   * @return mixed true on success
   */
  function parse($formula) {
    $this->_current_char = 0;
    $this->_formula = $formula;
    $this->_lookahead = isset($formula[1]) ? $formula[1] : '';
    $this
      ->_advance();
    $this->_parse_tree = $this
      ->_condition();
    return true;
  }

  /**
   * It parses a condition. It assumes the following rule:
   * Cond -> Expr [(">" | "<") Expr]
   *
   * @access private
   * @return mixed The parsed ptg'd tree on success
   */
  function _condition() {
    $result = $this
      ->_expression();
    if ($this->_current_token == "<") {
      $this
        ->_advance();
      $result2 = $this
        ->_expression();
      $result = $this
        ->_createTree('ptgLT', $result, $result2);
    }
    elseif ($this->_current_token == ">") {
      $this
        ->_advance();
      $result2 = $this
        ->_expression();
      $result = $this
        ->_createTree('ptgGT', $result, $result2);
    }
    elseif ($this->_current_token == "<=") {
      $this
        ->_advance();
      $result2 = $this
        ->_expression();
      $result = $this
        ->_createTree('ptgLE', $result, $result2);
    }
    elseif ($this->_current_token == ">=") {
      $this
        ->_advance();
      $result2 = $this
        ->_expression();
      $result = $this
        ->_createTree('ptgGE', $result, $result2);
    }
    elseif ($this->_current_token == "=") {
      $this
        ->_advance();
      $result2 = $this
        ->_expression();
      $result = $this
        ->_createTree('ptgEQ', $result, $result2);
    }
    elseif ($this->_current_token == "<>") {
      $this
        ->_advance();
      $result2 = $this
        ->_expression();
      $result = $this
        ->_createTree('ptgNE', $result, $result2);
    }
    elseif ($this->_current_token == "&") {
      $this
        ->_advance();
      $result2 = $this
        ->_expression();
      $result = $this
        ->_createTree('ptgConcat', $result, $result2);
    }
    return $result;
  }

  /**
   * It parses a expression. It assumes the following rule:
   * Expr -> Term [("+" | "-") Term]
   *      -> "string"
   *      -> "-" Term : Negative value
   *      -> "+" Term : Positive value
   *      -> Error code
   *
   * @access private
   * @return mixed The parsed ptg'd tree on success
   */
  function _expression() {

    // If it's a string return a string node
    if (preg_match("/\"([^\"]|\"\"){0,255}\"/", $this->_current_token)) {
      $tmp = str_replace('""', '"', $this->_current_token);
      if ($tmp == '"' || $tmp == '') {
        $tmp = '""';
      }

      //	Trap for "" that has been used for an empty string
      $result = $this
        ->_createTree($tmp, '', '');
      $this
        ->_advance();
      return $result;

      // If it's an error code
    }
    elseif (preg_match("/^#[A-Z0\\/]{3,5}[!?]{1}\$/", $this->_current_token) or $this->_current_token == '#N/A') {
      $result = $this
        ->_createTree($this->_current_token, 'ptgErr', '');
      $this
        ->_advance();
      return $result;

      // If it's a negative value
    }
    elseif ($this->_current_token == "-") {

      // catch "-" Term
      $this
        ->_advance();
      $result2 = $this
        ->_expression();
      $result = $this
        ->_createTree('ptgUminus', $result2, '');
      return $result;

      // If it's a positive value
    }
    elseif ($this->_current_token == "+") {

      // catch "+" Term
      $this
        ->_advance();
      $result2 = $this
        ->_expression();
      $result = $this
        ->_createTree('ptgUplus', $result2, '');
      return $result;
    }
    $result = $this
      ->_term();
    while ($this->_current_token == "+" or $this->_current_token == "-" or $this->_current_token == "^") {

      /**/
      if ($this->_current_token == "+") {
        $this
          ->_advance();
        $result2 = $this
          ->_term();
        $result = $this
          ->_createTree('ptgAdd', $result, $result2);
      }
      elseif ($this->_current_token == "-") {
        $this
          ->_advance();
        $result2 = $this
          ->_term();
        $result = $this
          ->_createTree('ptgSub', $result, $result2);
      }
      else {
        $this
          ->_advance();
        $result2 = $this
          ->_term();
        $result = $this
          ->_createTree('ptgPower', $result, $result2);
      }
    }
    return $result;
  }

  /**
   * This function just introduces a ptgParen element in the tree, so that Excel
   * doesn't get confused when working with a parenthesized formula afterwards.
   *
   * @access private
   * @see _fact()
   * @return array The parsed ptg'd tree
   */
  function _parenthesizedExpression() {
    $result = $this
      ->_createTree('ptgParen', $this
      ->_expression(), '');
    return $result;
  }

  /**
   * It parses a term. It assumes the following rule:
   * Term -> Fact [("*" | "/") Fact]
   *
   * @access private
   * @return mixed The parsed ptg'd tree on success
   */
  function _term() {
    $result = $this
      ->_fact();
    while ($this->_current_token == "*" or $this->_current_token == "/") {

      /**/
      if ($this->_current_token == "*") {
        $this
          ->_advance();
        $result2 = $this
          ->_fact();
        $result = $this
          ->_createTree('ptgMul', $result, $result2);
      }
      else {
        $this
          ->_advance();
        $result2 = $this
          ->_fact();
        $result = $this
          ->_createTree('ptgDiv', $result, $result2);
      }
    }
    return $result;
  }

  /**
   * It parses a factor. It assumes the following rule:
   * Fact -> ( Expr )
   *       | CellRef
   *       | CellRange
   *       | Number
   *       | Function
   *
   * @access private
   * @return mixed The parsed ptg'd tree on success
   */
  function _fact() {
    if ($this->_current_token == "(") {
      $this
        ->_advance();

      // eat the "("
      $result = $this
        ->_parenthesizedExpression();
      if ($this->_current_token != ")") {
        throw new PHPExcel_Writer_Exception("')' token expected.");
      }
      $this
        ->_advance();

      // eat the ")"
      return $result;
    }

    // if it's a reference
    if (preg_match('/^\\$?[A-Ia-i]?[A-Za-z]\\$?[0-9]+$/', $this->_current_token)) {
      $result = $this
        ->_createTree($this->_current_token, '', '');
      $this
        ->_advance();
      return $result;
    }
    elseif (preg_match("/^" . self::REGEX_SHEET_TITLE_UNQUOTED . "(\\:" . self::REGEX_SHEET_TITLE_UNQUOTED . ")?\\!\\\$?[A-Ia-i]?[A-Za-z]\\\$?[0-9]+\$/u", $this->_current_token)) {
      $result = $this
        ->_createTree($this->_current_token, '', '');
      $this
        ->_advance();
      return $result;
    }
    elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . "(\\:" . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\\$?[A-Ia-i]?[A-Za-z]\\\$?[0-9]+\$/u", $this->_current_token)) {
      $result = $this
        ->_createTree($this->_current_token, '', '');
      $this
        ->_advance();
      return $result;
    }
    elseif (preg_match('/^(\\$)?[A-Ia-i]?[A-Za-z](\\$)?[0-9]+:(\\$)?[A-Ia-i]?[A-Za-z](\\$)?[0-9]+$/', $this->_current_token) or preg_match('/^(\\$)?[A-Ia-i]?[A-Za-z](\\$)?[0-9]+\\.\\.(\\$)?[A-Ia-i]?[A-Za-z](\\$)?[0-9]+$/', $this->_current_token)) {

      // must be an error?
      $result = $this
        ->_createTree($this->_current_token, '', '');
      $this
        ->_advance();
      return $result;
    }
    elseif (preg_match("/^" . self::REGEX_SHEET_TITLE_UNQUOTED . "(\\:" . self::REGEX_SHEET_TITLE_UNQUOTED . ")?\\!\\\$?([A-Ia-i]?[A-Za-z])?\\\$?[0-9]+:\\\$?([A-Ia-i]?[A-Za-z])?\\\$?[0-9]+\$/u", $this->_current_token)) {

      // must be an error?

      //$result = $this->_current_token;
      $result = $this
        ->_createTree($this->_current_token, '', '');
      $this
        ->_advance();
      return $result;
    }
    elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . "(\\:" . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\\$?([A-Ia-i]?[A-Za-z])?\\\$?[0-9]+:\\\$?([A-Ia-i]?[A-Za-z])?\\\$?[0-9]+\$/u", $this->_current_token)) {

      // must be an error?

      //$result = $this->_current_token;
      $result = $this
        ->_createTree($this->_current_token, '', '');
      $this
        ->_advance();
      return $result;
    }
    elseif (is_numeric($this->_current_token)) {
      if ($this->_lookahead == '%') {
        $result = $this
          ->_createTree('ptgPercent', $this->_current_token, '');
        $this
          ->_advance();

        // Skip the percentage operator once we've pre-built that tree
      }
      else {
        $result = $this
          ->_createTree($this->_current_token, '', '');
      }
      $this
        ->_advance();
      return $result;
    }
    elseif (preg_match("", $this->_current_token)) {
      $result = $this
        ->_func();
      return $result;
    }
    throw new PHPExcel_Writer_Exception("Syntax error: " . $this->_current_token . ", lookahead: " . $this->_lookahead . ", current char: " . $this->_current_char);
  }

  /**
   * It parses a function call. It assumes the following rule:
   * Func -> ( Expr [,Expr]* )
   *
   * @access private
   * @return mixed The parsed ptg'd tree on success
   */
  function _func() {
    $num_args = 0;

    // number of arguments received
    $function = strtoupper($this->_current_token);
    $result = '';

    // initialize result
    $this
      ->_advance();
    $this
      ->_advance();

    // eat the "("
    while ($this->_current_token != ')') {

      /**/
      if ($num_args > 0) {
        if ($this->_current_token == "," or $this->_current_token == ";") {
          $this
            ->_advance();

          // eat the "," or ";"
        }
        else {
          throw new PHPExcel_Writer_Exception("Syntax error: comma expected in " . "function {$function}, arg #{$num_args}");
        }
        $result2 = $this
          ->_condition();
        $result = $this
          ->_createTree('arg', $result, $result2);
      }
      else {

        // first argument
        $result2 = $this
          ->_condition();
        $result = $this
          ->_createTree('arg', '', $result2);
      }
      ++$num_args;
    }
    if (!isset($this->_functions[$function])) {
      throw new PHPExcel_Writer_Exception("Function {$function}() doesn't exist");
    }
    $args = $this->_functions[$function][1];

    // If fixed number of args eg. TIME($i,$j,$k). Check that the number of args is valid.
    if ($args >= 0 and $args != $num_args) {
      throw new PHPExcel_Writer_Exception("Incorrect number of arguments in function {$function}() ");
    }
    $result = $this
      ->_createTree($function, $result, $num_args);
    $this
      ->_advance();

    // eat the ")"
    return $result;
  }

  /**
   * Creates a tree. In fact an array which may have one or two arrays (sub-trees)
   * as elements.
   *
   * @access private
   * @param mixed $value The value of this node.
   * @param mixed $left  The left array (sub-tree) or a final node.
   * @param mixed $right The right array (sub-tree) or a final node.
   * @return array A tree
   */
  function _createTree($value, $left, $right) {
    return array(
      'value' => $value,
      'left' => $left,
      'right' => $right,
    );
  }

  /**
   * Builds a string containing the tree in reverse polish notation (What you
   * would use in a HP calculator stack).
   * The following tree:
   *
   *    +
   *   / \
   *  2   3
   *
   * produces: "23+"
   *
   * The following tree:
   *
   *    +
   *   / \
   *  3   *
   *     / \
   *    6   A1
   *
   * produces: "36A1*+"
   *
   * In fact all operands, functions, references, etc... are written as ptg's
   *
   * @access public
   * @param array $tree The optional tree to convert.
   * @return string The tree in reverse polish notation
   */
  function toReversePolish($tree = array()) {
    $polish = "";

    // the string we are going to return
    if (empty($tree)) {

      // If it's the first call use _parse_tree
      $tree = $this->_parse_tree;
    }
    if (is_array($tree['left'])) {
      $converted_tree = $this
        ->toReversePolish($tree['left']);
      $polish .= $converted_tree;
    }
    elseif ($tree['left'] != '') {

      // It's a final node
      $converted_tree = $this
        ->_convert($tree['left']);
      $polish .= $converted_tree;
    }
    if (is_array($tree['right'])) {
      $converted_tree = $this
        ->toReversePolish($tree['right']);
      $polish .= $converted_tree;
    }
    elseif ($tree['right'] != '') {

      // It's a final node
      $converted_tree = $this
        ->_convert($tree['right']);
      $polish .= $converted_tree;
    }

    // if it's a function convert it here (so we can set it's arguments)
    if (preg_match("", $tree['value']) and !preg_match('/^([A-Ia-i]?[A-Za-z])(\\d+)$/', $tree['value']) and !preg_match("/^[A-Ia-i]?[A-Za-z](\\d+)\\.\\.[A-Ia-i]?[A-Za-z](\\d+)\$/", $tree['value']) and !is_numeric($tree['value']) and !isset($this->ptg[$tree['value']])) {

      // left subtree for a function is always an array.
      if ($tree['left'] != '') {
        $left_tree = $this
          ->toReversePolish($tree['left']);
      }
      else {
        $left_tree = '';
      }

      // add it's left subtree and return.
      return $left_tree . $this
        ->_convertFunction($tree['value'], $tree['right']);
    }
    else {
      $converted_tree = $this
        ->_convert($tree['value']);
    }
    $polish .= $converted_tree;
    return $polish;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
PHPExcel_Writer_Excel5_Parser::$_current_char public property * The index of the character we are currently looking at *
PHPExcel_Writer_Excel5_Parser::$_current_token public property * The token we are working on. *
PHPExcel_Writer_Excel5_Parser::$_ext_sheets public property * Array of external sheets *
PHPExcel_Writer_Excel5_Parser::$_formula public property * The formula to parse *
PHPExcel_Writer_Excel5_Parser::$_lookahead public property * The character ahead of the current char *
PHPExcel_Writer_Excel5_Parser::$_parse_tree public property * The parse tree to be generated *
PHPExcel_Writer_Excel5_Parser::$_references public property * Array of sheet references in the form of REF structures *
PHPExcel_Writer_Excel5_Parser::parse function * The parsing method. It parses a formula. * * @access public *
PHPExcel_Writer_Excel5_Parser::REGEX_SHEET_TITLE_QUOTED constant
PHPExcel_Writer_Excel5_Parser::REGEX_SHEET_TITLE_UNQUOTED constant
PHPExcel_Writer_Excel5_Parser::setExtSheet function * This method is used to update the array of sheet names. It is * called by the addWorksheet() method of the * PHPExcel_Writer_Excel5_Workbook class. * * @access public * *
PHPExcel_Writer_Excel5_Parser::toReversePolish function * Builds a string containing the tree in reverse polish notation (What you * would use in a HP calculator stack). * The following tree: * * + * / \ * 2 3 * * produces: "23+" * * The following tree: * * + …
PHPExcel_Writer_Excel5_Parser::_advance function * Advance to the next valid token. * * @access private
PHPExcel_Writer_Excel5_Parser::_cellToPackedRowcol function * pack() row and column into the required 3 or 4 byte format. * * @access private *
PHPExcel_Writer_Excel5_Parser::_cellToRowcol function * Convert an Excel cell reference such as A1 or $B2 or C$3 or $D$4 to a zero * indexed row and column number. Also returns two (0,1) values to indicate * whether the row or column are relative references. * * @access private *
PHPExcel_Writer_Excel5_Parser::_condition function * It parses a condition. It assumes the following rule: * Cond -> Expr [(">" | "<") Expr] * * @access private *
PHPExcel_Writer_Excel5_Parser::_convert function * Convert a token to the proper ptg value. * * @access private *
PHPExcel_Writer_Excel5_Parser::_convertError function Convert an error code to a ptgErr
PHPExcel_Writer_Excel5_Parser::_convertFunction function * Convert a function to a ptgFunc or ptgFuncVarV depending on the number of * args that it takes. * * @access private *
PHPExcel_Writer_Excel5_Parser::_convertNumber function * Convert a number token to ptgInt or ptgNum * * @access private *
PHPExcel_Writer_Excel5_Parser::_convertRange2d function * Convert an Excel range such as A1:D4 to a ptgRefV. * * @access private *
PHPExcel_Writer_Excel5_Parser::_convertRange3d function * Convert an Excel 3d range such as "Sheet1!A1:D4" or "Sheet1:Sheet2!A1:D4" to * a ptgArea3d. * * @access private *
PHPExcel_Writer_Excel5_Parser::_convertRef2d function * Convert an Excel reference such as A1, $B2, C$3 or $D$4 to a ptgRefV. * * @access private *
PHPExcel_Writer_Excel5_Parser::_convertRef3d function * Convert an Excel 3d reference such as "Sheet1!A1" or "Sheet1:Sheet2!A1" to a * ptgRef3d. * * @access private *
PHPExcel_Writer_Excel5_Parser::_convertString function * Convert a string token to ptgStr * * @access private *
PHPExcel_Writer_Excel5_Parser::_createTree function * Creates a tree. In fact an array which may have one or two arrays (sub-trees) * as elements. * * @access private *
PHPExcel_Writer_Excel5_Parser::_expression function * It parses a expression. It assumes the following rule: * Expr -> Term [("+" | "-") Term] * -> "string" * -> "-" Term : Negative value * -> "+" Term : Positive…
PHPExcel_Writer_Excel5_Parser::_fact function * It parses a factor. It assumes the following rule: * Fact -> ( Expr ) * | CellRef * | CellRange * | Number * | Function * * @access private *
PHPExcel_Writer_Excel5_Parser::_func function * It parses a function call. It assumes the following rule: * Func -> ( Expr [,Expr]* ) * * @access private *
PHPExcel_Writer_Excel5_Parser::_getRefIndex function * Look up the REF index that corresponds to an external sheet name * (or range). If it doesn't exist yet add it to the workbook's references * array. It assumes all sheet names given must exist. * * @access private *
PHPExcel_Writer_Excel5_Parser::_getSheetIndex function * Look up the index that corresponds to an external sheet name. The hash of * sheet names is updated by the addworksheet() method of the * PHPExcel_Writer_Excel5_Workbook class. * * @access private *
PHPExcel_Writer_Excel5_Parser::_initializeHashes function * Initialize the ptg and function hashes. * * @access private
PHPExcel_Writer_Excel5_Parser::_match function * Checks if it's a valid token. * * @access private *
PHPExcel_Writer_Excel5_Parser::_packExtRef function * Convert the sheet name part of an external reference, for example "Sheet1" or * "Sheet1:Sheet2", to a packed structure. * * @access private *
PHPExcel_Writer_Excel5_Parser::_parenthesizedExpression function * This function just introduces a ptgParen element in the tree, so that Excel * doesn't get confused when working with a parenthesized formula afterwards. * * @access private * *
PHPExcel_Writer_Excel5_Parser::_rangeToPackedRange function * pack() row range into the required 3 or 4 byte format. * Just using maximum col/rows, which is probably not the correct solution * * @access private *
PHPExcel_Writer_Excel5_Parser::_term function * It parses a term. It assumes the following rule: * Term -> Fact [("*" | "/") Fact] * * @access private *
PHPExcel_Writer_Excel5_Parser::__construct public function * The class constructor *