PHPExcel_Writer_Excel5
[ class tree: PHPExcel_Writer_Excel5 ] [ index: PHPExcel_Writer_Excel5 ] [ all elements ]

Source for file Parser.php

Documentation is available at Parser.php

  1. <?php
  2. /**
  3.  * PHPExcel
  4.  *
  5.  * Copyright (c) 2006 - 2011 PHPExcel
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2.1 of the License, or (at your option) any later version.
  11.  *
  12.  * This library is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * Lesser General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with this library; if not, write to the Free Software
  19.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  20.  *
  21.  * @category   PHPExcel
  22.  * @package    PHPExcel_Writer_Excel5
  23.  * @copyright  Copyright (c) 2006 - 2011 PHPExcel (http://www.codeplex.com/PHPExcel)
  24.  * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
  25.  * @version    1.7.6, 2011-02-27
  26.  */
  27.  
  28. // Original file header of PEAR::Spreadsheet_Excel_Writer_Parser (used as the base for this class):
  29. // -----------------------------------------------------------------------------------------
  30. // *  Class for parsing Excel formulas
  31. // *
  32. // *  License Information:
  33. // *
  34. // *    Spreadsheet_Excel_Writer:  A library for generating Excel Spreadsheets
  35. // *    Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
  36. // *
  37. // *    This library is free software; you can redistribute it and/or
  38. // *    modify it under the terms of the GNU Lesser General Public
  39. // *    License as published by the Free Software Foundation; either
  40. // *    version 2.1 of the License, or (at your option) any later version.
  41. // *
  42. // *    This library is distributed in the hope that it will be useful,
  43. // *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  44. // *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  45. // *    Lesser General Public License for more details.
  46. // *
  47. // *    You should have received a copy of the GNU Lesser General Public
  48. // *    License along with this library; if not, write to the Free Software
  49. // *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  50. // */
  51.  
  52.  
  53. /**
  54.  * PHPExcel_Writer_Excel5_Parser
  55.  *
  56.  * @category   PHPExcel
  57.  * @package    PHPExcel_Writer_Excel5
  58.  * @copyright  Copyright (c) 2006 - 2011 PHPExcel (http://www.codeplex.com/PHPExcel)
  59.  */
  60. {
  61.             const REGEX_SHEET_TITLE_UNQUOTED '[^\*\:\/\\\\\?\[\]\+\-\% \\\'\^\&\<\>\=\,\;\#\(\)\"\{\}]+';
  62.  
  63.     // Sheet title in quoted form (without surrounding quotes)
  64.     // Invalid sheet title characters cannot occur in the sheet title:
  65.     // *:/\?[]                    (usual invalid sheet title characters)
  66.     // Single quote is represented as a pair ''
  67.     const REGEX_SHEET_TITLE_QUOTED '(([^\*\:\/\\\\\?\[\]\\\'])+|(\\\'\\\')+)+';
  68.  
  69.     /**
  70.      * The index of the character we are currently looking at
  71.      * @var integer 
  72.      */
  73.     public $_current_char;
  74.  
  75.     /**
  76.      * The token we are working on.
  77.      * @var string 
  78.      */
  79.     public $_current_token;
  80.  
  81.     /**
  82.      * The formula to parse
  83.      * @var string 
  84.      */
  85.     public $_formula;
  86.  
  87.     /**
  88.      * The character ahead of the current char
  89.      * @var string 
  90.      */
  91.     public $_lookahead;
  92.  
  93.     /**
  94.      * The parse tree to be generated
  95.      * @var string 
  96.      */
  97.     public $_parse_tree;
  98.  
  99.     /**
  100.      * Array of external sheets
  101.      * @var array 
  102.      */
  103.     public $_ext_sheets;
  104.  
  105.     /**
  106.      * Array of sheet references in the form of REF structures
  107.      * @var array 
  108.      */
  109.     public $_references;
  110.  
  111.     /**
  112.      * The BIFF version for the workbook
  113.      * @var integer 
  114.      */
  115.     public $_BIFF_version;
  116.  
  117.     /**
  118.      * The class constructor
  119.      *
  120.      * @param integer $byte_order The byte order (Little endian or Big endian) of the architecture
  121.      *                            (optional). 1 => big endian, 0 (default) little endian.
  122.      */
  123.     public function __construct($biff_version)
  124.     {
  125.         $this->_current_char  = 0;
  126.         $this->_BIFF_version  = $biff_version;
  127.         $this->_current_token = '';       // The token we are working on.
  128.         $this->_formula       = '';       // The formula to parse.
  129.         $this->_lookahead     = '';       // The character ahead of the current char.
  130.         $this->_parse_tree    = '';       // The parse tree to be generated.
  131.         $this->_initializeHashes();      // Initialize the hashes: ptg's and function's ptg's
  132.         $this->_ext_sheets = array();
  133.         $this->_references = array();
  134.     }
  135.  
  136.     /**
  137.      * Initialize the ptg and function hashes.
  138.      *
  139.      * @access private
  140.      */
  141.     function _initializeHashes()
  142.     {
  143.         // The Excel ptg indices
  144.         $this->ptg array(
  145.             'ptgExp'       => 0x01,
  146.             'ptgTbl'       => 0x02,
  147.             'ptgAdd'       => 0x03,
  148.             'ptgSub'       => 0x04,
  149.             'ptgMul'       => 0x05,
  150.             'ptgDiv'       => 0x06,
  151.             'ptgPower'     => 0x07,
  152.             'ptgConcat'    => 0x08,
  153.             'ptgLT'        => 0x09,
  154.             'ptgLE'        => 0x0A,
  155.             'ptgEQ'        => 0x0B,
  156.             'ptgGE'        => 0x0C,
  157.             'ptgGT'        => 0x0D,
  158.             'ptgNE'        => 0x0E,
  159.             'ptgIsect'     => 0x0F,
  160.             'ptgUnion'     => 0x10,
  161.             'ptgRange'     => 0x11,
  162.             'ptgUplus'     => 0x12,
  163.             'ptgUminus'    => 0x13,
  164.             'ptgPercent'   => 0x14,
  165.             'ptgParen'     => 0x15,
  166.             'ptgMissArg'   => 0x16,
  167.             'ptgStr'       => 0x17,
  168.             'ptgAttr'      => 0x19,
  169.             'ptgSheet'     => 0x1A,
  170.             'ptgEndSheet'  => 0x1B,
  171.             'ptgErr'       => 0x1C,
  172.             'ptgBool'      => 0x1D,
  173.             'ptgInt'       => 0x1E,
  174.             'ptgNum'       => 0x1F,
  175.             'ptgArray'     => 0x20,
  176.             'ptgFunc'      => 0x21,
  177.             'ptgFuncVar'   => 0x22,
  178.             'ptgName'      => 0x23,
  179.             'ptgRef'       => 0x24,
  180.             'ptgArea'      => 0x25,
  181.             'ptgMemArea'   => 0x26,
  182.             'ptgMemErr'    => 0x27,
  183.             'ptgMemNoMem'  => 0x28,
  184.             'ptgMemFunc'   => 0x29,
  185.             'ptgRefErr'    => 0x2A,
  186.             'ptgAreaErr'   => 0x2B,
  187.             'ptgRefN'      => 0x2C,
  188.             'ptgAreaN'     => 0x2D,
  189.             'ptgMemAreaN'  => 0x2E,
  190.             'ptgMemNoMemN' => 0x2F,
  191.             'ptgNameX'     => 0x39,
  192.             'ptgRef3d'     => 0x3A,
  193.             'ptgArea3d'    => 0x3B,
  194.             'ptgRefErr3d'  => 0x3C,
  195.             'ptgAreaErr3d' => 0x3D,
  196.             'ptgArrayV'    => 0x40,
  197.             'ptgFuncV'     => 0x41,
  198.             'ptgFuncVarV'  => 0x42,
  199.             'ptgNameV'     => 0x43,
  200.             'ptgRefV'      => 0x44,
  201.             'ptgAreaV'     => 0x45,
  202.             'ptgMemAreaV'  => 0x46,
  203.             'ptgMemErrV'   => 0x47,
  204.             'ptgMemNoMemV' => 0x48,
  205.             'ptgMemFuncV'  => 0x49,
  206.             'ptgRefErrV'   => 0x4A,
  207.             'ptgAreaErrV'  => 0x4B,
  208.             'ptgRefNV'     => 0x4C,
  209.             'ptgAreaNV'    => 0x4D,
  210.             'ptgMemAreaNV' => 0x4E,
  211.             'ptgMemNoMemN' => 0x4F,
  212.             'ptgFuncCEV'   => 0x58,
  213.             'ptgNameXV'    => 0x59,
  214.             'ptgRef3dV'    => 0x5A,
  215.             'ptgArea3dV'   => 0x5B,
  216.             'ptgRefErr3dV' => 0x5C,
  217.             'ptgAreaErr3d' => 0x5D,
  218.             'ptgArrayA'    => 0x60,
  219.             'ptgFuncA'     => 0x61,
  220.             'ptgFuncVarA'  => 0x62,
  221.             'ptgNameA'     => 0x63,
  222.             'ptgRefA'      => 0x64,
  223.             'ptgAreaA'     => 0x65,
  224.             'ptgMemAreaA'  => 0x66,
  225.             'ptgMemErrA'   => 0x67,
  226.             'ptgMemNoMemA' => 0x68,
  227.             'ptgMemFuncA'  => 0x69,
  228.             'ptgRefErrA'   => 0x6A,
  229.             'ptgAreaErrA'  => 0x6B,
  230.             'ptgRefNA'     => 0x6C,
  231.             'ptgAreaNA'    => 0x6D,
  232.             'ptgMemAreaNA' => 0x6E,
  233.             'ptgMemNoMemN' => 0x6F,
  234.             'ptgFuncCEA'   => 0x78,
  235.             'ptgNameXA'    => 0x79,
  236.             'ptgRef3dA'    => 0x7A,
  237.             'ptgArea3dA'   => 0x7B,
  238.             'ptgRefErr3dA' => 0x7C,
  239.             'ptgAreaErr3d' => 0x7D
  240.             );
  241.  
  242.         // Thanks to Michael Meeks and Gnumeric for the initial arg values.
  243.         //
  244.         // The following hash was generated by "function_locale.pl" in the distro.
  245.         // Refer to function_locale.pl for non-English function names.
  246.         //
  247.         // The array elements are as follow:
  248.         // ptg:   The Excel function ptg code.
  249.         // args:  The number of arguments that the function takes:
  250.         //           >=0 is a fixed number of arguments.
  251.         //           -1  is a variable  number of arguments.
  252.         // class: The reference, value or array class of the function args.
  253.         // vol:   The function is volatile.
  254.         //
  255.         $this->_functions array(
  256.               // function                  ptg  args  class  vol
  257.               'COUNT'           => array(   0,   -1,    0,    ),
  258.               'IF'              => array(   1,   -1,    1,    ),
  259.               'ISNA'            => array(   2,    1,    1,    ),
  260.               'ISERROR'         => array(   3,    1,    1,    ),
  261.               'SUM'             => array(   4,   -1,    0,    ),
  262.               'AVERAGE'         => array(   5,   -1,    0,    ),
  263.               'MIN'             => array(   6,   -1,    0,    ),
  264.               'MAX'             => array(   7,   -1,    0,    ),
  265.               'ROW'             => array(   8,   -1,    0,    ),
  266.               'COLUMN'          => array(   9,   -1,    0,    ),
  267.               'NA'              => array(  10,    0,    0,    ),
  268.               'NPV'             => array(  11,   -1,    1,    ),
  269.               'STDEV'           => array(  12,   -1,    0,    ),
  270.               'DOLLAR'          => array(  13,   -1,    1,    ),
  271.               'FIXED'           => array(  14,   -1,    1,    ),
  272.               'SIN'             => array(  15,    1,    1,    ),
  273.               'COS'             => array(  16,    1,    1,    ),
  274.               'TAN'             => array(  17,    1,    1,    ),
  275.               'ATAN'            => array(  18,    1,    1,    ),
  276.               'PI'              => array(  19,    0,    1,    ),
  277.               'SQRT'            => array(  20,    1,    1,    ),
  278.               'EXP'             => array(  21,    1,    1,    ),
  279.               'LN'              => array(  22,    1,    1,    ),
  280.               'LOG10'           => array(  23,    1,    1,    ),
  281.               'ABS'             => array(  24,    1,    1,    ),
  282.               'INT'             => array(  25,    1,    1,    ),
  283.               'SIGN'            => array(  26,    1,    1,    ),
  284.               'ROUND'           => array(  27,    2,    1,    ),
  285.               'LOOKUP'          => array(  28,   -1,    0,    ),
  286.               'INDEX'           => array(  29,   -1,    0,    ),
  287.               'REPT'            => array(  30,    2,    1,    ),
  288.               'MID'             => array(  31,    3,    1,    ),
  289.               'LEN'             => array(  32,    1,    1,    ),
  290.               'VALUE'           => array(  33,    1,    1,    ),
  291.               'TRUE'            => array(  34,    0,    1,    ),
  292.               'FALSE'           => array(  35,    0,    1,    ),
  293.               'AND'             => array(  36,   -1,    0,    ),
  294.               'OR'              => array(  37,   -1,    0,    ),
  295.               'NOT'             => array(  38,    1,    1,    ),
  296.               'MOD'             => array(  39,    2,    1,    ),
  297.               'DCOUNT'          => array(  40,    3,    0,    ),
  298.               'DSUM'            => array(  41,    3,    0,    ),
  299.               'DAVERAGE'        => array(  42,    3,    0,    ),
  300.               'DMIN'            => array(  43,    3,    0,    ),
  301.               'DMAX'            => array(  44,    3,    0,    ),
  302.               'DSTDEV'          => array(  45,    3,    0,    ),
  303.               'VAR'             => array(  46,   -1,    0,    ),
  304.               'DVAR'            => array(  47,    3,    0,    ),
  305.               'TEXT'            => array(  48,    2,    1,    ),
  306.               'LINEST'          => array(  49,   -1,    0,    ),
  307.               'TREND'           => array(  50,   -1,    0,    ),
  308.               'LOGEST'          => array(  51,   -1,    0,    ),
  309.               'GROWTH'          => array(  52,   -1,    0,    ),
  310.               'PV'              => array(  56,   -1,    1,    ),
  311.               'FV'              => array(  57,   -1,    1,    ),
  312.               'NPER'            => array(  58,   -1,    1,    ),
  313.               'PMT'             => array(  59,   -1,    1,    ),
  314.               'RATE'            => array(  60,   -1,    1,    ),
  315.               'MIRR'            => array(  61,    3,    0,    ),
  316.               'IRR'             => array(  62,   -1,    0,    ),
  317.               'RAND'            => array(  63,    0,    1,    ),
  318.               'MATCH'           => array(  64,   -1,    0,    ),
  319.               'DATE'            => array(  65,    3,    1,    ),
  320.               'TIME'            => array(  66,    3,    1,    ),
  321.               'DAY'             => array(  67,    1,    1,    ),
  322.               'MONTH'           => array(  68,    1,    1,    ),
  323.               'YEAR'            => array(  69,    1,    1,    ),
  324.               'WEEKDAY'         => array(  70,   -1,    1,    ),
  325.               'HOUR'            => array(  71,    1,    1,    ),
  326.               'MINUTE'          => array(  72,    1,    1,    ),
  327.               'SECOND'          => array(  73,    1,    1,    ),
  328.               'NOW'             => array(  74,    0,    1,    ),
  329.               'AREAS'           => array(  75,    1,    0,    ),
  330.               'ROWS'            => array(  76,    1,    0,    ),
  331.               'COLUMNS'         => array(  77,    1,    0,    ),
  332.               'OFFSET'          => array(  78,   -1,    0,    ),
  333.               'SEARCH'          => array(  82,   -1,    1,    ),
  334.               'TRANSPOSE'       => array(  83,    1,    1,    ),
  335.               'TYPE'            => array(  86,    1,    1,    ),
  336.               'ATAN2'           => array(  97,    2,    1,    ),
  337.               'ASIN'            => array(  98,    1,    1,    ),
  338.               'ACOS'            => array(  99,    1,    1,    ),
  339.               'CHOOSE'          => array100,   -1,    1,    ),
  340.               'HLOOKUP'         => array101,   -1,    0,    ),
  341.               'VLOOKUP'         => array102,   -1,    0,    ),
  342.               'ISREF'           => array105,    1,    0,    ),
  343.               'LOG'             => array109,   -1,    1,    ),
  344.               'CHAR'            => array111,    1,    1,    ),
  345.               'LOWER'           => array112,    1,    1,    ),
  346.               'UPPER'           => array113,    1,    1,    ),
  347.               'PROPER'          => array114,    1,    1,    ),
  348.               'LEFT'            => array115,   -1,    1,    ),
  349.               'RIGHT'           => array116,   -1,    1,    ),
  350.               'EXACT'           => array117,    2,    1,    ),
  351.               'TRIM'            => array118,    1,    1,    ),
  352.               'REPLACE'         => array119,    4,    1,    ),
  353.               'SUBSTITUTE'      => array120,   -1,    1,    ),
  354.               'CODE'            => array121,    1,    1,    ),
  355.               'FIND'            => array124,   -1,    1,    ),
  356.               'CELL'            => array125,   -1,    0,    ),
  357.               'ISERR'           => array126,    1,    1,    ),
  358.               'ISTEXT'          => array127,    1,    1,    ),
  359.               'ISNUMBER'        => array128,    1,    1,    ),
  360.               'ISBLANK'         => array129,    1,    1,    ),
  361.               'T'               => array130,    1,    0,    ),
  362.               'N'               => array131,    1,    0,    ),
  363.               'DATEVALUE'       => array140,    1,    1,    ),
  364.               'TIMEVALUE'       => array141,    1,    1,    ),
  365.               'SLN'             => array142,    3,    1,    ),
  366.               'SYD'             => array143,    4,    1,    ),
  367.               'DDB'             => array144,   -1,    1,    ),
  368.               'INDIRECT'        => array148,   -1,    1,    ),
  369.               'CALL'            => array150,   -1,    1,    ),
  370.               'CLEAN'           => array162,    1,    1,    ),
  371.               'MDETERM'         => array163,    1,    2,    ),
  372.               'MINVERSE'        => array164,    1,    2,    ),
  373.               'MMULT'           => array165,    2,    2,    ),
  374.               'IPMT'            => array167,   -1,    1,    ),
  375.               'PPMT'            => array168,   -1,    1,    ),
  376.               'COUNTA'          => array169,   -1,    0,    ),
  377.               'PRODUCT'         => array183,   -1,    0,    ),
  378.               'FACT'            => array184,    1,    1,    ),
  379.               'DPRODUCT'        => array189,    3,    0,    ),
  380.               'ISNONTEXT'       => array190,    1,    1,    ),
  381.               'STDEVP'          => array193,   -1,    0,    ),
  382.               'VARP'            => array194,   -1,    0,    ),
  383.               'DSTDEVP'         => array195,    3,    0,    ),
  384.               'DVARP'           => array196,    3,    0,    ),
  385.               'TRUNC'           => array197,   -1,    1,    ),
  386.               'ISLOGICAL'       => array198,    1,    1,    ),
  387.               'DCOUNTA'         => array199,    3,    0,    ),
  388.               'USDOLLAR'        => array204,   -1,    1,    ),
  389.               'FINDB'           => array205,   -1,    1,    ),
  390.               'SEARCHB'         => array206,   -1,    1,    ),
  391.               'REPLACEB'        => array207,    4,    1,    ),
  392.               'LEFTB'           => array208,   -1,    1,    ),
  393.               'RIGHTB'          => array209,   -1,    1,    ),
  394.               'MIDB'            => array210,    3,    1,    ),
  395.               'LENB'            => array211,    1,    1,    ),
  396.               'ROUNDUP'         => array212,    2,    1,    ),
  397.               'ROUNDDOWN'       => array213,    2,    1,    ),
  398.               'ASC'             => array214,    1,    1,    ),
  399.               'DBCS'            => array215,    1,    1,    ),
  400.               'RANK'            => array216,   -1,    0,    ),
  401.               'ADDRESS'         => array219,   -1,    1,    ),
  402.               'DAYS360'         => array220,   -1,    1,    ),
  403.               'TODAY'           => array221,    0,    1,    ),
  404.               'VDB'             => array222,   -1,    1,    ),
  405.               'MEDIAN'          => array227,   -1,    0,    ),
  406.               'SUMPRODUCT'      => array228,   -1,    2,    ),
  407.               'SINH'            => array229,    1,    1,    ),
  408.               'COSH'            => array230,    1,    1,    ),
  409.               'TANH'            => array231,    1,    1,    ),
  410.               'ASINH'           => array232,    1,    1,    ),
  411.               'ACOSH'           => array233,    1,    1,    ),
  412.               'ATANH'           => array234,    1,    1,    ),
  413.               'DGET'            => array235,    3,    0,    ),
  414.               'INFO'            => array244,    1,    1,    ),
  415.               'DB'              => array247,   -1,    1,    ),
  416.               'FREQUENCY'       => array252,    2,    0,    ),
  417.               'ERROR.TYPE'      => array261,    1,    1,    ),
  418.               'REGISTER.ID'     => array267,   -1,    1,    ),
  419.               'AVEDEV'          => array269,   -1,    0,    ),
  420.               'BETADIST'        => array270,   -1,    1,    ),
  421.               'GAMMALN'         => array271,    1,    1,    ),
  422.               'BETAINV'         => array272,   -1,    1,    ),
  423.               'BINOMDIST'       => array273,    4,    1,    ),
  424.               'CHIDIST'         => array274,    2,    1,    ),
  425.               'CHIINV'          => array275,    2,    1,    ),
  426.               'COMBIN'          => array276,    2,    1,    ),
  427.               'CONFIDENCE'      => array277,    3,    1,    ),
  428.               'CRITBINOM'       => array278,    3,    1,    ),
  429.               'EVEN'            => array279,    1,    1,    ),
  430.               'EXPONDIST'       => array280,    3,    1,    ),
  431.               'FDIST'           => array281,    3,    1,    ),
  432.               'FINV'            => array282,    3,    1,    ),
  433.               'FISHER'          => array283,    1,    1,    ),
  434.               'FISHERINV'       => array284,    1,    1,    ),
  435.               'FLOOR'           => array285,    2,    1,    ),
  436.               'GAMMADIST'       => array286,    4,    1,    ),
  437.               'GAMMAINV'        => array287,    3,    1,    ),
  438.               'CEILING'         => array288,    2,    1,    ),
  439.               'HYPGEOMDIST'     => array289,    4,    1,    ),
  440.               'LOGNORMDIST'     => array290,    3,    1,    ),
  441.               'LOGINV'          => array291,    3,    1,    ),
  442.               'NEGBINOMDIST'    => array292,    3,    1,    ),
  443.               'NORMDIST'        => array293,    4,    1,    ),
  444.               'NORMSDIST'       => array294,    1,    1,    ),
  445.               'NORMINV'         => array295,    3,    1,    ),
  446.               'NORMSINV'        => array296,    1,    1,    ),
  447.               'STANDARDIZE'     => array297,    3,    1,    ),
  448.               'ODD'             => array298,    1,    1,    ),
  449.               'PERMUT'          => array299,    2,    1,    ),
  450.               'POISSON'         => array300,    3,    1,    ),
  451.               'TDIST'           => array301,    3,    1,    ),
  452.               'WEIBULL'         => array302,    4,    1,    ),
  453.               'SUMXMY2'         => array303,    2,    2,    ),
  454.               'SUMX2MY2'        => array304,    2,    2,    ),
  455.               'SUMX2PY2'        => array305,    2,    2,    ),
  456.               'CHITEST'         => array306,    2,    2,    ),
  457.               'CORREL'          => array307,    2,    2,    ),
  458.               'COVAR'           => array308,    2,    2,    ),
  459.               'FORECAST'        => array309,    3,    2,    ),
  460.               'FTEST'           => array310,    2,    2,    ),
  461.               'INTERCEPT'       => array311,    2,    2,    ),
  462.               'PEARSON'         => array312,    2,    2,    ),
  463.               'RSQ'             => array313,    2,    2,    ),
  464.               'STEYX'           => array314,    2,    2,    ),
  465.               'SLOPE'           => array315,    2,    2,    ),
  466.               'TTEST'           => array316,    4,    2,    ),
  467.               'PROB'            => array317,   -1,    2,    ),
  468.               'DEVSQ'           => array318,   -1,    0,    ),
  469.               'GEOMEAN'         => array319,   -1,    0,    ),
  470.               'HARMEAN'         => array320,   -1,    0,    ),
  471.               'SUMSQ'           => array321,   -1,    0,    ),
  472.               'KURT'            => array322,   -1,    0,    ),
  473.               'SKEW'            => array323,   -1,    0,    ),
  474.               'ZTEST'           => array324,   -1,    0,    ),
  475.               'LARGE'           => array325,    2,    0,    ),
  476.               'SMALL'           => array326,    2,    0,    ),
  477.               'QUARTILE'        => array327,    2,    0,    ),
  478.               'PERCENTILE'      => array328,    2,    0,    ),
  479.               'PERCENTRANK'     => array329,   -1,    0,    ),
  480.               'MODE'            => array330,   -1,    2,    ),
  481.               'TRIMMEAN'        => array331,    2,    0,    ),
  482.               'TINV'            => array332,    2,    1,    ),
  483.               'CONCATENATE'     => array336,   -1,    1,    ),
  484.               'POWER'           => array337,    2,    1,    ),
  485.               'RADIANS'         => array342,    1,    1,    ),
  486.               'DEGREES'         => array343,    1,    1,    ),
  487.               'SUBTOTAL'        => array344,   -1,    0,    ),
  488.               'SUMIF'           => array345,   -1,    0,    ),
  489.               'COUNTIF'         => array346,    2,    0,    ),
  490.               'COUNTBLANK'      => array347,    1,    0,    ),
  491.               'ISPMT'           => array350,    4,    1,    ),
  492.               'DATEDIF'         => array351,    3,    1,    ),
  493.               'DATESTRING'      => array352,    1,    1,    ),
  494.               'NUMBERSTRING'    => array353,    2,    1,    ),
  495.               'ROMAN'           => array354,   -1,    1,    ),
  496.               'GETPIVOTDATA'    => array358,   -1,    0,    ),
  497.               'HYPERLINK'       => array359,   -1,    1,    ),
  498.               'PHONETIC'        => array360,    1,    0,    ),
  499.               'AVERAGEA'        => array361,   -1,    0,    ),
  500.               'MAXA'            => array362,   -1,    0,    ),
  501.               'MINA'            => array363,   -1,    0,    ),
  502.               'STDEVPA'         => array364,   -1,    0,    ),
  503.               'VARPA'           => array365,   -1,    0,    ),
  504.               'STDEVA'          => array366,   -1,    0,    ),
  505.               'VARA'            => array367,   -1,    0,    ),
  506.               'BAHTTEXT'        => array368,    1,    0,    ),
  507.               );
  508.     }
  509.  
  510.     /**
  511.      * Convert a token to the proper ptg value.
  512.      *
  513.      * @access private
  514.      * @param mixed $token The token to convert.
  515.      * @return mixed the converted token on success
  516.      */
  517.     function _convert($token)
  518.     {
  519.         if (preg_match("/\"([^\"]|\"\"){0,255}\"/"$token)) {
  520.             return $this->_convertString($token);
  521.  
  522.         elseif (is_numeric($token)) {
  523.             return $this->_convertNumber($token);
  524.  
  525.         // match references like A1 or $A$1
  526.         elseif (preg_match('/^\$?([A-Ia-i]?[A-Za-z])\$?(\d+)$/',$token)) {
  527.             return $this->_convertRef2d($token);
  528.  
  529.         // match external references like Sheet1!A1 or Sheet1:Sheet2!A1 or Sheet1!$A$1 or Sheet1:Sheet2!$A$1
  530.         elseif (preg_match("/^" self::REGEX_SHEET_TITLE_UNQUOTED "(\:" self::REGEX_SHEET_TITLE_UNQUOTED ")?\!\\$?[A-Ia-i]?[A-Za-z]\\$?(\d+)$/u",$token)) {
  531.             return $this->_convertRef3d($token);
  532.  
  533.         // match external references like 'Sheet1'!A1 or 'Sheet1:Sheet2'!A1 or 'Sheet1'!$A$1 or 'Sheet1:Sheet2'!$A$1
  534.         elseif (preg_match("/^'" self::REGEX_SHEET_TITLE_QUOTED "(\:" self::REGEX_SHEET_TITLE_QUOTED ")?'\!\\$?[A-Ia-i]?[A-Za-z]\\$?(\d+)$/u",$token)) {
  535.             return $this->_convertRef3d($token);
  536.  
  537.         // match ranges like A1:B2 or $A$1:$B$2
  538.         elseif (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)\:(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)$/'$token)) {
  539.             return $this->_convertRange2d($token);
  540.  
  541.         // 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
  542.         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)) {
  543.             return $this->_convertRange3d($token);
  544.  
  545.         // 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
  546.         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)) {
  547.             return $this->_convertRange3d($token);
  548.  
  549.         // operators (including parentheses)
  550.         elseif (isset($this->ptg[$token])) {
  551.             return pack("C"$this->ptg[$token]);
  552.  
  553.         // match error codes
  554.         elseif (preg_match("/^#[A-Z0\/]{3,5}[!?]{1}$/"$tokenor $token == '#N/A'{
  555.             return $this->_convertError($token);
  556.  
  557.         // commented so argument number can be processed correctly. See toReversePolish().
  558.         /*elseif (preg_match("/[A-Z0-9\xc0-\xdc\.]+/",$token))
  559.         {
  560.             return($this->_convertFunction($token,$this->_func_args));
  561.         }*/
  562.  
  563.         // if it's an argument, ignore the token (the argument remains)
  564.         elseif ($token == 'arg'{
  565.             return '';
  566.         }
  567.  
  568.         // TODO: use real error codes
  569.         throw new Exception("Unknown token $token");
  570.     }
  571.  
  572.     /**
  573.      * Convert a number token to ptgInt or ptgNum
  574.      *
  575.      * @access private
  576.      * @param mixed $num an integer or double for conversion to its ptg value
  577.      */
  578.     function _convertNumber($num)
  579.     {
  580.         // Integer in the range 0..2**16-1
  581.         if ((preg_match("/^\d+$/"$num)) and ($num <= 65535)) {
  582.             return pack("Cv"$this->ptg['ptgInt']$num);
  583.         else // A float
  584.             if (PHPExcel_Writer_Excel5_BIFFwriter::getByteOrder()) // if it's Big Endian
  585.                 $num strrev($num);
  586.             }
  587.             return pack("Cd"$this->ptg['ptgNum']$num);
  588.         }
  589.     }
  590.  
  591.     /**
  592.      * Convert a string token to ptgStr
  593.      *
  594.      * @access private
  595.      * @param string $string A string for conversion to its ptg value.
  596.      * @return mixed the converted token on success
  597.      */
  598.     function _convertString($string)
  599.     {
  600.         // chop away beggining and ending quotes
  601.         $string substr($string1strlen($string2);
  602.         if (strlen($string255{
  603.             throw new Exception("String is too long");
  604.         }
  605.  
  606.         if ($this->_BIFF_version == 0x0500{
  607.             return pack("CC"$this->ptg['ptgStr']strlen($string)).$string;
  608.         elseif ($this->_BIFF_version == 0x0600{
  609.             return pack('C'$this->ptg['ptgStr']PHPExcel_Shared_String::UTF8toBIFF8UnicodeShort($string);
  610.         }
  611.     }
  612.  
  613.     /**
  614.      * Convert a function to a ptgFunc or ptgFuncVarV depending on the number of
  615.      * args that it takes.
  616.      *
  617.      * @access private
  618.      * @param string  $token    The name of the function for convertion to ptg value.
  619.      * @param integer $num_args The number of arguments the function receives.
  620.      * @return string The packed ptg for the function
  621.      */
  622.     function _convertFunction($token$num_args)
  623.     {
  624.         $args     $this->_functions[$token][1];
  625.         $volatile $this->_functions[$token][3];
  626.  
  627.         // Fixed number of args eg. TIME($i,$j,$k).
  628.         if ($args >= 0{
  629.             return pack("Cv"$this->ptg['ptgFuncV']$this->_functions[$token][0]);
  630.         }
  631.         // Variable number of args eg. SUM($i,$j,$k, ..).
  632.         if ($args == -1{
  633.             return pack("CCv"$this->ptg['ptgFuncVarV']$num_args$this->_functions[$token][0]);
  634.         }
  635.     }
  636.  
  637.     /**
  638.      * Convert an Excel range such as A1:D4 to a ptgRefV.
  639.      *
  640.      * @access private
  641.      * @param string $range An Excel range in the A1:A2
  642.      */
  643.     function _convertRange2d($range$class=0)
  644.     {
  645.  
  646.         // TODO: possible class value 0,1,2 check Formula.pm
  647.         // Split the range into 2 cell refs
  648.         if (preg_match('/^(\$)?([A-Ia-i]?[A-Za-z])(\$)?(\d+)\:(\$)?([A-Ia-i]?[A-Za-z])(\$)?(\d+)$/'$range)) {
  649.             list($cell1$cell2explode(':'$range);
  650.         else {
  651.             // TODO: use real error codes
  652.             throw new Exception("Unknown range separator");
  653.         }
  654.  
  655.         // Convert the cell references
  656.         $cell_array1 $this->_cellToPackedRowcol($cell1);
  657.         list($row1$col1$cell_array1;
  658.         $cell_array2 $this->_cellToPackedRowcol($cell2);
  659.         list($row2$col2$cell_array2;
  660.  
  661.         // The ptg value depends on the class of the ptg.
  662.         if ($class == 0{
  663.             $ptgArea pack("C"$this->ptg['ptgArea']);
  664.         elseif ($class == 1{
  665.             $ptgArea pack("C"$this->ptg['ptgAreaV']);
  666.         elseif ($class == 2{
  667.             $ptgArea pack("C"$this->ptg['ptgAreaA']);
  668.         else {
  669.             // TODO: use real error codes
  670.             throw new Exception("Unknown class $class");
  671.         }
  672.         return $ptgArea $row1 $row2 $col1$col2;
  673.     }
  674.  
  675.     /**
  676.      * Convert an Excel 3d range such as "Sheet1!A1:D4" or "Sheet1:Sheet2!A1:D4" to
  677.      * a ptgArea3d.
  678.      *
  679.      * @access private
  680.      * @param string $token An Excel range in the Sheet1!A1:A2 format.
  681.      * @return mixed The packed ptgArea3d token on success.
  682.      */
  683.     function _convertRange3d($token)
  684.     {
  685.         $class 0// formulas like Sheet1!$A$1:$A$2 in list type data validation need this class (0x3B)
  686.  
  687.         // Split the ref at the ! symbol
  688.         list($ext_ref$rangeexplode('!'$token);
  689.  
  690.         // Convert the external reference part (different for BIFF8)
  691.         if ($this->_BIFF_version == 0x0500{
  692.             $ext_ref $this->_packExtRef($ext_ref);
  693.         elseif ($this->_BIFF_version == 0x0600{
  694.              $ext_ref $this->_getRefIndex($ext_ref);
  695.         }
  696.  
  697.         // Split the range into 2 cell refs
  698.         list($cell1$cell2explode(':'$range);
  699.  
  700.         // Convert the cell references
  701.         if (preg_match("/^(\\$)?[A-Ia-i]?[A-Za-z](\\$)?(\d+)$/"$cell1)) {
  702.             $cell_array1 $this->_cellToPackedRowcol($cell1);
  703.             list($row1$col1$cell_array1;
  704.             $cell_array2 $this->_cellToPackedRowcol($cell2);
  705.             list($row2$col2$cell_array2;
  706.         else // It's a rows range (like 26:27)
  707.              $cells_array $this->_rangeToPackedRange($cell1.':'.$cell2);
  708.              list($row1$col1$row2$col2$cells_array;
  709.         }
  710.  
  711.         // The ptg value depends on the class of the ptg.
  712.         if ($class == 0{
  713.             $ptgArea pack("C"$this->ptg['ptgArea3d']);
  714.         elseif ($class == 1{
  715.             $ptgArea pack("C"$this->ptg['ptgArea3dV']);
  716.         elseif ($class == 2{
  717.             $ptgArea pack("C"$this->ptg['ptgArea3dA']);
  718.         else {
  719.             throw new Exception("Unknown class $class");
  720.         }
  721.  
  722.         return $ptgArea $ext_ref $row1 $row2 $col1$col2;
  723.     }
  724.  
  725.     /**
  726.      * Convert an Excel reference such as A1, $B2, C$3 or $D$4 to a ptgRefV.
  727.      *
  728.      * @access private
  729.      * @param string $cell An Excel cell reference
  730.      * @return string The cell in packed() format with the corresponding ptg
  731.      */
  732.     function _convertRef2d($cell)
  733.     {
  734.         $class 2// as far as I know, this is magick.
  735.  
  736.         // Convert the cell reference
  737.         $cell_array $this->_cellToPackedRowcol($cell);
  738.         list($row$col$cell_array;
  739.  
  740.         // The ptg value depends on the class of the ptg.
  741.         if ($class == 0{
  742.             $ptgRef pack("C"$this->ptg['ptgRef']);
  743.         elseif ($class == 1{
  744.             $ptgRef pack("C"$this->ptg['ptgRefV']);
  745.         elseif ($class == 2{
  746.             $ptgRef pack("C"$this->ptg['ptgRefA']);
  747.         else {
  748.             // TODO: use real error codes
  749.             throw new Exception("Unknown class $class");
  750.         }
  751.         return $ptgRef.$row.$col;
  752.     }
  753.  
  754.     /**
  755.      * Convert an Excel 3d reference such as "Sheet1!A1" or "Sheet1:Sheet2!A1" to a
  756.      * ptgRef3d.
  757.      *
  758.      * @access private
  759.      * @param string $cell An Excel cell reference
  760.      * @return mixed The packed ptgRef3d token on success.
  761.      */
  762.     function _convertRef3d($cell)
  763.     {
  764.         $class 2// as far as I know, this is magick.
  765.  
  766.         // Split the ref at the ! symbol
  767.         list($ext_ref$cellexplode('!'$cell);
  768.  
  769.         // Convert the external reference part (different for BIFF8)
  770.         if ($this->_BIFF_version == 0x0500{
  771.             $ext_ref $this->_packExtRef($ext_ref);
  772.         elseif ($this->_BIFF_version == 0x0600{
  773.             $ext_ref $this->_getRefIndex($ext_ref);
  774.         }
  775.  
  776.         // Convert the cell reference part
  777.         list($row$col$this->_cellToPackedRowcol($cell);
  778.  
  779.         // The ptg value depends on the class of the ptg.
  780.         if ($class == 0{
  781.             $ptgRef pack("C"$this->ptg['ptgRef3d']);
  782.         elseif ($class == 1{
  783.             $ptgRef pack("C"$this->ptg['ptgRef3dV']);
  784.         elseif ($class == 2{
  785.             $ptgRef pack("C"$this->ptg['ptgRef3dA']);
  786.         else {
  787.             throw new Exception("Unknown class $class");
  788.         }
  789.  
  790.         return $ptgRef $ext_ref$row $col;
  791.     }
  792.  
  793.     /**
  794.      * Convert an error code to a ptgErr
  795.      *
  796.      * @access private
  797.      * @param mixed $num an error codefor conversion to its ptg value
  798.      */
  799.     function _convertError($errorCode)
  800.     {
  801.         switch ($errorCode{
  802.             case '#NULL!':    return pack("C"0x00);
  803.             case '#DIV/0!':    return pack("C"0x07);
  804.             case '#VALUE!':    return pack("C"0x0F);
  805.             case '#REF!':    return pack("C"0x17);
  806.             case '#NAME?':    return pack("C"0x1D);
  807.             case '#NUM!':    return pack("C"0x24);
  808.             case '#N/A':    return pack("C"0x2A);
  809.         }
  810.         return pack("C"0xFF);
  811.     }
  812.  
  813.     /**
  814.      * Convert the sheet name part of an external reference, for example "Sheet1" or
  815.      * "Sheet1:Sheet2", to a packed structure.
  816.      *
  817.      * @access private
  818.      * @param string $ext_ref The name of the external reference
  819.      * @return string The reference index in packed() format
  820.      */
  821.     function _packExtRef($ext_ref)
  822.     {
  823.         $ext_ref preg_replace("/^'/"''$ext_ref)// Remove leading  ' if any.
  824.         $ext_ref preg_replace("/'$/"''$ext_ref)// Remove trailing ' if any.
  825.  
  826.         // Check if there is a sheet range eg., Sheet1:Sheet2.
  827.         if (preg_match("/:/"$ext_ref)) {
  828.             list($sheet_name1$sheet_name2explode(':'$ext_ref);
  829.  
  830.             $sheet1 $this->_getSheetIndex($sheet_name1);
  831.             if ($sheet1 == -1{
  832.                 throw new Exception("Unknown sheet name $sheet_name1 in formula");
  833.             }
  834.             $sheet2 $this->_getSheetIndex($sheet_name2);
  835.             if ($sheet2 == -1{
  836.                 throw new Exception("Unknown sheet name $sheet_name2 in formula");
  837.             }
  838.  
  839.             // Reverse max and min sheet numbers if necessary
  840.             if ($sheet1 $sheet2{
  841.                 list($sheet1$sheet2array($sheet2$sheet1);
  842.             }
  843.         else // Single sheet name only.
  844.             $sheet1 $this->_getSheetIndex($ext_ref);
  845.             if ($sheet1 == -1{
  846.                 throw new Exception("Unknown sheet name $ext_ref in formula");
  847.             }
  848.             $sheet2 $sheet1;
  849.         }
  850.  
  851.         // References are stored relative to 0xFFFF.
  852.         $offset = -$sheet1;
  853.  
  854.         return pack('vdvv'$offset0x00$sheet1$sheet2);
  855.     }
  856.  
  857.     /**
  858.      * Look up the REF index that corresponds to an external sheet name
  859.      * (or range). If it doesn't exist yet add it to the workbook's references
  860.      * array. It assumes all sheet names given must exist.
  861.      *
  862.      * @access private
  863.      * @param string $ext_ref The name of the external reference
  864.      * @return mixed The reference index in packed() format on success
  865.      */
  866.     function _getRefIndex($ext_ref)
  867.     {
  868.         $ext_ref preg_replace("/^'/"''$ext_ref)// Remove leading  ' if any.
  869.         $ext_ref preg_replace("/'$/"''$ext_ref)// Remove trailing ' if any.
  870.         $ext_ref str_replace('\'\'''\''$ext_ref)// Replace escaped '' with '
  871.  
  872.         // Check if there is a sheet range eg., Sheet1:Sheet2.
  873.         if (preg_match("/:/"$ext_ref)) {
  874.             list($sheet_name1$sheet_name2explode(':'$ext_ref);
  875.  
  876.             $sheet1 $this->_getSheetIndex($sheet_name1);
  877.             if ($sheet1 == -1{
  878.                 throw new Exception("Unknown sheet name $sheet_name1 in formula");
  879.             }
  880.             $sheet2 $this->_getSheetIndex($sheet_name2);
  881.             if ($sheet2 == -1{
  882.                 throw new Exception("Unknown sheet name $sheet_name2 in formula");
  883.             }
  884.  
  885.             // Reverse max and min sheet numbers if necessary
  886.             if ($sheet1 $sheet2{
  887.                 list($sheet1$sheet2array($sheet2$sheet1);
  888.             }
  889.         else // Single sheet name only.
  890.             $sheet1 $this->_getSheetIndex($ext_ref);
  891.             if ($sheet1 == -1{
  892.                 throw new Exception("Unknown sheet name $ext_ref in formula");
  893.             }
  894.             $sheet2 $sheet1;
  895.         }
  896.  
  897.         // assume all references belong to this document
  898.         $supbook_index 0x00;
  899.         $ref pack('vvv'$supbook_index$sheet1$sheet2);
  900.         $total_references count($this->_references);
  901.         $index = -1;
  902.         for ($i 0$i $total_references++$i{
  903.             if ($ref == $this->_references[$i]{
  904.                 $index $i;
  905.                 break;
  906.             }
  907.         }
  908.         // if REF was not found add it to references array
  909.         if ($index == -1{
  910.             $this->_references[$total_references$ref;
  911.             $index $total_references;
  912.         }
  913.  
  914.         return pack('v'$index);
  915.     }
  916.  
  917.     /**
  918.      * Look up the index that corresponds to an external sheet name. The hash of
  919.      * sheet names is updated by the addworksheet() method of the
  920.      * PHPExcel_Writer_Excel5_Workbook class.
  921.      *
  922.      * @access private
  923.      * @return integer The sheet index, -1 if the sheet was not found
  924.      */
  925.     function _getSheetIndex($sheet_name)
  926.     {
  927.         if (!isset($this->_ext_sheets[$sheet_name])) {
  928.             return -1;
  929.         else {
  930.             return $this->_ext_sheets[$sheet_name];
  931.         }
  932.     }
  933.  
  934.     /**
  935.      * This method is used to update the array of sheet names. It is
  936.      * called by the addWorksheet() method of the
  937.      * PHPExcel_Writer_Excel5_Workbook class.
  938.      *
  939.      * @access public
  940.      * @see PHPExcel_Writer_Excel5_Workbook::addWorksheet()
  941.      * @param string  $name  The name of the worksheet being added
  942.      * @param integer $index The index of the worksheet being added
  943.      */
  944.     function setExtSheet($name$index)
  945.     {
  946.         $this->_ext_sheets[$name$index;
  947.     }
  948.  
  949.     /**
  950.      * pack() row and column into the required 3 or 4 byte format.
  951.      *
  952.      * @access private
  953.      * @param string $cell The Excel cell reference to be packed
  954.      * @return array Array containing the row and column in packed() format
  955.      */
  956.     function _cellToPackedRowcol($cell)
  957.     {
  958.         $cell strtoupper($cell);
  959.         list($row$col$row_rel$col_rel$this->_cellToRowcol($cell);
  960.         if ($col >= 256{
  961.             throw new Exception("Column in: $cell greater than 255");
  962.         }
  963.         // FIXME: change for BIFF8
  964.         if ($row >= 16384{
  965.             throw new Exception("Row in: $cell greater than 16384 ");
  966.         }
  967.  
  968.         // Set the high bits to indicate if row or col are relative.
  969.         if ($this->_BIFF_version == 0x0500{
  970.             $row    |= $col_rel << 14;
  971.             $row    |= $row_rel << 15;
  972.             $col     pack('C'$col);
  973.         elseif ($this->_BIFF_version == 0x0600{
  974.             $col    |= $col_rel << 14;
  975.             $col    |= $row_rel << 15;
  976.             $col     pack('v'$col);
  977.         }
  978.         $row     pack('v'$row);
  979.  
  980.         return array($row$col);
  981.     }
  982.  
  983.     /**
  984.      * pack() row range into the required 3 or 4 byte format.
  985.      * Just using maximum col/rows, which is probably not the correct solution
  986.      *
  987.      * @access private
  988.      * @param string $range The Excel range to be packed
  989.      * @return array Array containing (row1,col1,row2,col2) in packed() format
  990.      */
  991.     function _rangeToPackedRange($range)
  992.     {
  993.         preg_match('/(\$)?(\d+)\:(\$)?(\d+)/'$range$match);
  994.         // return absolute rows if there is a $ in the ref
  995.         $row1_rel empty($match[1]0;
  996.         $row1     $match[2];
  997.         $row2_rel empty($match[3]0;
  998.         $row2     $match[4];
  999.         // Convert 1-index to zero-index
  1000.         --$row1;
  1001.         --$row2;
  1002.         // Trick poor inocent Excel
  1003.         $col1 0;
  1004.         $col2 16383// FIXME: maximum possible value for Excel 5 (change this!!!)
  1005.  
  1006.         // FIXME: this changes for BIFF8
  1007.         if (($row1 >= 16384or ($row2 >= 16384)) {
  1008.             throw new Exception("Row in: $range greater than 16384 ");
  1009.         }
  1010.  
  1011.         // Set the high bits to indicate if rows are relative.
  1012.         if ($this->_BIFF_version == 0x0500{
  1013.             $row1    |= $row1_rel << 14// FIXME: probably a bug
  1014.             $row2    |= $row2_rel << 15;
  1015.             $col1     pack('C'$col1);
  1016.             $col2     pack('C'$col2);
  1017.         elseif ($this->_BIFF_version == 0x0600{
  1018.             $col1    |= $row1_rel << 15;
  1019.             $col2    |= $row2_rel << 15;
  1020.             $col1     pack('v'$col1);
  1021.             $col2     pack('v'$col2);
  1022.         }
  1023.         $row1     pack('v'$row1);
  1024.         $row2     pack('v'$row2);
  1025.  
  1026.         return array($row1$col1$row2$col2);
  1027.     }
  1028.  
  1029.     /**
  1030.      * Convert an Excel cell reference such as A1 or $B2 or C$3 or $D$4 to a zero
  1031.      * indexed row and column number. Also returns two (0,1) values to indicate
  1032.      * whether the row or column are relative references.
  1033.      *
  1034.      * @access private
  1035.      * @param string $cell The Excel cell reference in A1 format.
  1036.      * @return array 
  1037.      */
  1038.     function _cellToRowcol($cell)
  1039.     {
  1040.         preg_match('/(\$)?([A-I]?[A-Z])(\$)?(\d+)/',$cell,$match);
  1041.         // return absolute column if there is a $ in the ref
  1042.         $col_rel empty($match[1]0;
  1043.         $col_ref $match[2];
  1044.         $row_rel empty($match[3]0;
  1045.         $row     $match[4];
  1046.  
  1047.         // Convert base26 column string to a number.
  1048.         $expn   strlen($col_ref1;
  1049.         $col    0;
  1050.         $col_ref_length strlen($col_ref);
  1051.         for ($i 0$i $col_ref_length++$i{
  1052.             $col += (ord($col_ref{$i}64pow(26$expn);
  1053.             --$expn;
  1054.         }
  1055.  
  1056.         // Convert 1-index to zero-index
  1057.         --$row;
  1058.         --$col;
  1059.  
  1060.         return array($row$col$row_rel$col_rel);
  1061.     }
  1062.  
  1063.     /**
  1064.      * Advance to the next valid token.
  1065.      *
  1066.      * @access private
  1067.      */
  1068.     function _advance()
  1069.     {
  1070.         $i $this->_current_char;
  1071.         $formula_length strlen($this->_formula);
  1072.         // eat up white spaces
  1073.         if ($i $formula_length{
  1074.             while ($this->_formula{$i== " "{
  1075.                 ++$i;
  1076.             }
  1077.  
  1078.             if ($i ($formula_length 1)) {
  1079.                 $this->_lookahead = $this->_formula{$i+1};
  1080.             }
  1081.             $token '';
  1082.         }
  1083.  
  1084.         while ($i $formula_length{
  1085.             $token .= $this->_formula{$i};
  1086.             if ($i ($formula_length 1)) {
  1087.                 $this->_lookahead $this->_formula{$i+1};
  1088.             else {
  1089.                 $this->_lookahead '';
  1090.             }
  1091.  
  1092.             if ($this->_match($token!= ''{
  1093.                 //if ($i < strlen($this->_formula) - 1) {
  1094.                 //    $this->_lookahead = $this->_formula{$i+1};
  1095.                 //}
  1096.                 $this->_current_char $i 1;
  1097.                 $this->_current_token $token;
  1098.                 return 1;
  1099.             }
  1100.  
  1101.             if ($i ($formula_length 2)) {
  1102.                 $this->_lookahead $this->_formula{$i+2};
  1103.             else // if we run out of characters _lookahead becomes empty
  1104.                 $this->_lookahead '';
  1105.             }
  1106.             ++$i;
  1107.         }
  1108.         //die("Lexical error ".$this->_current_char);
  1109.     }
  1110.  
  1111.     /**
  1112.      * Checks if it's a valid token.
  1113.      *
  1114.      * @access private
  1115.      * @param mixed $token The token to check.
  1116.      * @return mixed       The checked token or false on failure
  1117.      */
  1118.     function _match($token)
  1119.     {
  1120.         switch($token{
  1121.             case "+":
  1122.             case "-":
  1123.             case "*":
  1124.             case "/":
  1125.             case "(":
  1126.             case ")":
  1127.             case ",":
  1128.             case ";":
  1129.             case ">=":
  1130.             case "<=":
  1131.             case "=":
  1132.             case "<>":
  1133.             case "^":
  1134.             case "&":
  1135.             case "%":
  1136.                 return $token;
  1137.                 break;
  1138.             case ">":
  1139.                 if ($this->_lookahead == '='// it's a GE token
  1140.                     break;
  1141.                 }
  1142.                 return $token;
  1143.                 break;
  1144.             case "<":
  1145.                 // it's a LE or a NE token
  1146.                 if (($this->_lookahead == '='or ($this->_lookahead == '>')) {
  1147.                     break;
  1148.                 }
  1149.                 return $token;
  1150.                 break;
  1151.             default:
  1152.                 // if it's a reference A1 or $A$1 or $A1 or A$1
  1153.                 if (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?[0-9]+$/',$tokenand
  1154.                    !preg_match("/[0-9]/",$this->_lookaheadand
  1155.                    ($this->_lookahead != ':'and ($this->_lookahead != '.'and
  1156.                    ($this->_lookahead != '!'))
  1157.                 {
  1158.                     return $token;
  1159.                 }
  1160.                 // If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1 or Sheet1!$A$1 or Sheet1:Sheet2!$A$1)
  1161.                 elseif (preg_match("/^" self::REGEX_SHEET_TITLE_UNQUOTED "(\:" self::REGEX_SHEET_TITLE_UNQUOTED ")?\!\\$?[A-Ia-i]?[A-Za-z]\\$?[0-9]+$/u",$tokenand
  1162.                        !preg_match("/[0-9]/",$this->_lookaheadand
  1163.                        ($this->_lookahead != ':'and ($this->_lookahead != '.'))
  1164.                 {
  1165.                     return $token;
  1166.                 }
  1167.                 // If it's an external reference ('Sheet1'!A1 or 'Sheet1:Sheet2'!A1 or 'Sheet1'!$A$1 or 'Sheet1:Sheet2'!$A$1)
  1168.                 elseif (preg_match("/^'" self::REGEX_SHEET_TITLE_QUOTED "(\:" self::REGEX_SHEET_TITLE_QUOTED ")?'\!\\$?[A-Ia-i]?[A-Za-z]\\$?[0-9]+$/u",$tokenand
  1169.                        !preg_match("/[0-9]/",$this->_lookaheadand
  1170.                        ($this->_lookahead != ':'and ($this->_lookahead != '.'))
  1171.                 {
  1172.                     return $token;
  1173.                 }
  1174.                 // if it's a range A1:A2 or $A$1:$A$2
  1175.                 elseif (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+:(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+$/'$tokenand
  1176.                        !preg_match("/[0-9]/",$this->_lookahead))
  1177.                 {
  1178.                     return $token;
  1179.                 }
  1180.                 // If it's an external range like Sheet1!A1:B2 or Sheet1:Sheet2!A1:B2 or Sheet1!$A$1:$B$2 or Sheet1:Sheet2!$A$1:$B$2
  1181.                 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",$tokenand
  1182.                        !preg_match("/[0-9]/",$this->_lookahead))
  1183.                 {
  1184.                     return $token;
  1185.                 }
  1186.                 // If it's an external range like 'Sheet1'!A1:B2 or 'Sheet1:Sheet2'!A1:B2 or 'Sheet1'!$A$1:$B$2 or 'Sheet1:Sheet2'!$A$1:$B$2
  1187.                 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",$tokenand
  1188.                        !preg_match("/[0-9]/",$this->_lookahead))
  1189.                 {
  1190.                     return $token;
  1191.                 }
  1192.                 // If it's a number (check that it's not a sheet name or range)
  1193.                 elseif (is_numeric($tokenand
  1194.                         (!is_numeric($token.$this->_lookaheador ($this->_lookahead == '')) and
  1195.                         ($this->_lookahead != '!'and ($this->_lookahead != ':'))
  1196.                 {
  1197.                     return $token;
  1198.                 }
  1199.                 // If it's a string (of maximum 255 characters)
  1200.                 elseif (preg_match("/\"([^\"]|\"\"){0,255}\"/",$tokenand $this->_lookahead != '"' and (substr_count($token'"')%== 0))
  1201.                 {
  1202.                     return $token;
  1203.                 }
  1204.                 // If it's an error code
  1205.                 elseif (preg_match("/^#[A-Z0\/]{3,5}[!?]{1}$/"$tokenor $token == '#N/A')
  1206.                 {
  1207.                     return $token;
  1208.                 }
  1209.                 // if it's a function call
  1210.                 elseif (preg_match("/^[A-Z0-9\xc0-\xdc\.]+$/i",$tokenand ($this->_lookahead == "("))
  1211.                 {
  1212.                     return $token;
  1213.                 }
  1214.                 return '';
  1215.         }
  1216.     }
  1217.  
  1218.     /**
  1219.      * The parsing method. It parses a formula.
  1220.      *
  1221.      * @access public
  1222.      * @param string $formula The formula to parse, without the initial equal
  1223.      *                         sign (=).
  1224.      * @return mixed true on success
  1225.      */
  1226.     function parse($formula)
  1227.     {
  1228.         $this->_current_char 0;
  1229.         $this->_formula      $formula;
  1230.         $this->_lookahead    = isset($formula{1}$formula{1'';
  1231.         $this->_advance();
  1232.         $this->_parse_tree   $this->_condition();
  1233.         return true;
  1234.     }
  1235.  
  1236.     /**
  1237.      * It parses a condition. It assumes the following rule:
  1238.      * Cond -> Expr [(">" | "<") Expr]
  1239.      *
  1240.      * @access private
  1241.      * @return mixed The parsed ptg'd tree on success
  1242.      */
  1243.     function _condition()
  1244.     {
  1245.         $result $this->_expression();
  1246.         if ($this->_current_token == "<"{
  1247.             $this->_advance();
  1248.             $result2 $this->_expression();
  1249.             $result $this->_createTree('ptgLT'$result$result2);
  1250.         elseif ($this->_current_token == ">"{
  1251.             $this->_advance();
  1252.             $result2 $this->_expression();
  1253.             $result $this->_createTree('ptgGT'$result$result2);
  1254.         elseif ($this->_current_token == "<="{
  1255.             $this->_advance();
  1256.             $result2 $this->_expression();
  1257.             $result $this->_createTree('ptgLE'$result$result2);
  1258.         elseif ($this->_current_token == ">="{
  1259.             $this->_advance();
  1260.             $result2 $this->_expression();
  1261.             $result $this->_createTree('ptgGE'$result$result2);
  1262.         elseif ($this->_current_token == "="{
  1263.             $this->_advance();
  1264.             $result2 $this->_expression();
  1265.             $result $this->_createTree('ptgEQ'$result$result2);
  1266.         elseif ($this->_current_token == "<>"{
  1267.             $this->_advance();
  1268.             $result2 $this->_expression();
  1269.             $result $this->_createTree('ptgNE'$result$result2);
  1270.         elseif ($this->_current_token == "&"{
  1271.             $this->_advance();
  1272.             $result2 $this->_expression();
  1273.             $result $this->_createTree('ptgConcat'$result$result2);
  1274.         }
  1275.         return $result;
  1276.     }
  1277.  
  1278.     /**
  1279.      * It parses a expression. It assumes the following rule:
  1280.      * Expr -> Term [("+" | "-") Term]
  1281.      *      -> "string"
  1282.      *      -> "-" Term : Negative value
  1283.      *      -> "+" Term : Positive value
  1284.      *      -> Error code
  1285.      *
  1286.      * @access private
  1287.      * @return mixed The parsed ptg'd tree on success
  1288.      */
  1289.     function _expression()
  1290.     {
  1291.         // If it's a string return a string node
  1292.         if (preg_match("/\"([^\"]|\"\"){0,255}\"/"$this->_current_token)) {
  1293.             $tmp str_replace('""''"'$this->_current_token);
  1294.             if (($tmp == '"'|| ($tmp == '')) $tmp '""';    //    Trap for "" that has been used for an empty string
  1295.             $result $this->_createTree($tmp'''');
  1296.             $this->_advance();
  1297.             return $result;
  1298.         // If it's an error code
  1299.         elseif (preg_match("/^#[A-Z0\/]{3,5}[!?]{1}$/"$this->_current_tokenor $this->_current_token == '#N/A'){
  1300.             $result $this->_createTree($this->_current_token'ptgErr''');
  1301.             $this->_advance();
  1302.             return $result;
  1303.         // If it's a negative value
  1304.         elseif ($this->_current_token == "-"{
  1305.             // catch "-" Term
  1306.             $this->_advance();
  1307.             $result2 $this->_expression();
  1308.             $result $this->_createTree('ptgUminus'$result2'');
  1309.             return $result;
  1310.         // If it's a positive value
  1311.         elseif ($this->_current_token == "+"{
  1312.             // catch "+" Term
  1313.             $this->_advance();
  1314.             $result2 $this->_expression();
  1315.             $result $this->_createTree('ptgUplus'$result2'');
  1316.             return $result;
  1317.         }
  1318.         $result $this->_term();
  1319.         while (($this->_current_token == "+"or
  1320.                ($this->_current_token == "-"or
  1321.                ($this->_current_token == "^")) {
  1322.         /**/
  1323.             if ($this->_current_token == "+"{
  1324.                 $this->_advance();
  1325.                 $result2 $this->_term();
  1326.                 $result $this->_createTree('ptgAdd'$result$result2);
  1327.             elseif ($this->_current_token == "-"{
  1328.                 $this->_advance();
  1329.                 $result2 $this->_term();
  1330.                 $result $this->_createTree('ptgSub'$result$result2);
  1331.             else {
  1332.                 $this->_advance();
  1333.                 $result2 $this->_term();
  1334.                 $result $this->_createTree('ptgPower'$result$result2);
  1335.             }
  1336.         }
  1337.         return $result;
  1338.     }
  1339.  
  1340.     /**
  1341.      * This function just introduces a ptgParen element in the tree, so that Excel
  1342.      * doesn't get confused when working with a parenthesized formula afterwards.
  1343.      *
  1344.      * @access private
  1345.      * @see _fact()
  1346.      * @return array The parsed ptg'd tree
  1347.      */
  1348.     function _parenthesizedExpression()
  1349.     {
  1350.         $result $this->_createTree('ptgParen'$this->_expression()'');
  1351.         return $result;
  1352.     }
  1353.  
  1354.     /**
  1355.      * It parses a term. It assumes the following rule:
  1356.      * Term -> Fact [("*" | "/") Fact]
  1357.      *
  1358.      * @access private
  1359.      * @return mixed The parsed ptg'd tree on success
  1360.      */
  1361.     function _term()
  1362.     {
  1363.         $result $this->_fact();
  1364.         while (($this->_current_token == "*"or
  1365.                ($this->_current_token == "/")) {
  1366.         /**/
  1367.             if ($this->_current_token == "*"{
  1368.                 $this->_advance();
  1369.                 $result2 $this->_fact();
  1370.                 $result $this->_createTree('ptgMul'$result$result2);
  1371.             else {
  1372.                 $this->_advance();
  1373.                 $result2 $this->_fact();
  1374.                 $result $this->_createTree('ptgDiv'$result$result2);
  1375.             }
  1376.         }
  1377.         return $result;
  1378.     }
  1379.  
  1380.     /**
  1381.      * It parses a factor. It assumes the following rule:
  1382.      * Fact -> ( Expr )
  1383.      *       | CellRef
  1384.      *       | CellRange
  1385.      *       | Number
  1386.      *       | Function
  1387.      *
  1388.      * @access private
  1389.      * @return mixed The parsed ptg'd tree on success
  1390.      */
  1391.     function _fact()
  1392.     {
  1393.         if ($this->_current_token == "("{
  1394.             $this->_advance();         // eat the "("
  1395.             $result $this->_parenthesizedExpression();
  1396.             if ($this->_current_token != ")"{
  1397.                 throw new Exception("')' token expected.");
  1398.             }
  1399.             $this->_advance();         // eat the ")"
  1400.             return $result;
  1401.         }
  1402.         // if it's a reference
  1403.         if (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?[0-9]+$/',$this->_current_token))
  1404.         {
  1405.             $result $this->_createTree($this->_current_token'''');
  1406.             $this->_advance();
  1407.             return $result;
  1408.         }
  1409.         // If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1 or Sheet1!$A$1 or Sheet1:Sheet2!$A$1)
  1410.         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))
  1411.         {
  1412.             $result $this->_createTree($this->_current_token'''');
  1413.             $this->_advance();
  1414.             return $result;
  1415.         }
  1416.         // If it's an external reference ('Sheet1'!A1 or 'Sheet1:Sheet2'!A1 or 'Sheet1'!$A$1 or 'Sheet1:Sheet2'!$A$1)
  1417.         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))
  1418.         {
  1419.             $result $this->_createTree($this->_current_token'''');
  1420.             $this->_advance();
  1421.             return $result;
  1422.         }
  1423.         // if it's a range A1:B2 or $A$1:$B$2
  1424.         elseif (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+:(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+$/',$this->_current_tokenor
  1425.                 preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+\.\.(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+$/',$this->_current_token))
  1426.         {
  1427.             // must be an error?
  1428.             $result $this->_createTree($this->_current_token'''');
  1429.             $this->_advance();
  1430.             return $result;
  1431.         }
  1432.         // If it's an external range (Sheet1!A1:B2 or Sheet1:Sheet2!A1:B2 or Sheet1!$A$1:$B$2 or Sheet1:Sheet2!$A$1:$B$2)
  1433.         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))
  1434.         {
  1435.             // must be an error?
  1436.             //$result = $this->_current_token;
  1437.             $result $this->_createTree($this->_current_token'''');
  1438.             $this->_advance();
  1439.             return $result;
  1440.         }
  1441.         // If it's an external range ('Sheet1'!A1:B2 or 'Sheet1'!A1:B2 or 'Sheet1'!$A$1:$B$2 or 'Sheet1'!$A$1:$B$2)
  1442.         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))
  1443.         {
  1444.             // must be an error?
  1445.             //$result = $this->_current_token;
  1446.             $result $this->_createTree($this->_current_token'''');
  1447.             $this->_advance();
  1448.             return $result;
  1449.         }
  1450.         // If it's a number or a percent
  1451.         elseif (is_numeric($this->_current_token))
  1452.         {
  1453.             if($this->_lookahead == '%'){
  1454.                 $result $this->_createTree('ptgPercent'$this->_current_token'');
  1455.             else {
  1456.                 $result $this->_createTree($this->_current_token'''');
  1457.             }
  1458.             $this->_advance();
  1459.             return $result;
  1460.         }
  1461.         // if it's a function call
  1462.         elseif (preg_match("/^[A-Z0-9\xc0-\xdc\.]+$/i",$this->_current_token))
  1463.         {
  1464.             $result $this->_func();
  1465.             return $result;
  1466.         }
  1467.         throw new Exception("Syntax error: ".$this->_current_token.
  1468.                                  ", lookahead: ".$this->_lookahead.
  1469.                                  ", current char: ".$this->_current_char);
  1470.     }
  1471.  
  1472.     /**
  1473.      * It parses a function call. It assumes the following rule:
  1474.      * Func -> ( Expr [,Expr]* )
  1475.      *
  1476.      * @access private
  1477.      * @return mixed The parsed ptg'd tree on success
  1478.      */
  1479.     function _func()
  1480.     {
  1481.         $num_args 0// number of arguments received
  1482.         $function strtoupper($this->_current_token);
  1483.         $result   ''// initialize result
  1484.         $this->_advance();
  1485.         $this->_advance();         // eat the "("
  1486.         while ($this->_current_token != ')'{
  1487.         /**/
  1488.             if ($num_args 0{
  1489.                 if ($this->_current_token == "," or
  1490.                     $this->_current_token == ";")
  1491.                 {
  1492.                     $this->_advance();  // eat the "," or ";"
  1493.                 else {
  1494.                     throw new Exception("Syntax error: comma expected in ".
  1495.                                       "function $function, arg #{$num_args}");
  1496.                 }
  1497.                 $result2 $this->_condition();
  1498.                 $result $this->_createTree('arg'$result$result2);
  1499.             else // first argument
  1500.                 $result2 $this->_condition();
  1501.                 $result $this->_createTree('arg'''$result2);
  1502.             }
  1503.             ++$num_args;
  1504.         }
  1505.         if (!isset($this->_functions[$function])) {
  1506.             throw new Exception("Function $function() doesn't exist");
  1507.         }
  1508.         $args $this->_functions[$function][1];
  1509.         // If fixed number of args eg. TIME($i,$j,$k). Check that the number of args is valid.
  1510.         if (($args >= 0and ($args != $num_args)) {
  1511.             throw new Exception("Incorrect number of arguments in function $function() ");
  1512.         }
  1513.  
  1514.         $result $this->_createTree($function$result$num_args);
  1515.         $this->_advance();         // eat the ")"
  1516.         return $result;
  1517.     }
  1518.  
  1519.     /**
  1520.      * Creates a tree. In fact an array which may have one or two arrays (sub-trees)
  1521.      * as elements.
  1522.      *
  1523.      * @access private
  1524.      * @param mixed $value The value of this node.
  1525.      * @param mixed $left  The left array (sub-tree) or a final node.
  1526.      * @param mixed $right The right array (sub-tree) or a final node.
  1527.      * @return array A tree
  1528.      */
  1529.     function _createTree($value$left$right)
  1530.     {
  1531.         return array('value' => $value'left' => $left'right' => $right);
  1532.     }
  1533.  
  1534.     /**
  1535.      * Builds a string containing the tree in reverse polish notation (What you
  1536.      * would use in a HP calculator stack).
  1537.      * The following tree:
  1538.      *
  1539.      *    +
  1540.      *   / \
  1541.      *  2   3
  1542.      *
  1543.      * produces: "23+"
  1544.      *
  1545.      * The following tree:
  1546.      *
  1547.      *    +
  1548.      *   / \
  1549.      *  3   *
  1550.      *     / \
  1551.      *    6   A1
  1552.      *
  1553.      * produces: "36A1*+"
  1554.      *
  1555.      * In fact all operands, functions, references, etc... are written as ptg's
  1556.      *
  1557.      * @access public
  1558.      * @param array $tree The optional tree to convert.
  1559.      * @return string The tree in reverse polish notation
  1560.      */
  1561.     function toReversePolish($tree array())
  1562.     {
  1563.         $polish ""// the string we are going to return
  1564.         if (empty($tree)) // If it's the first call use _parse_tree
  1565.             $tree $this->_parse_tree;
  1566.         }
  1567.  
  1568.         if (is_array($tree['left'])) {
  1569.             $converted_tree $this->toReversePolish($tree['left']);
  1570.             $polish .= $converted_tree;
  1571.         elseif ($tree['left'!= ''// It's a final node
  1572.             $converted_tree $this->_convert($tree['left']);
  1573.             $polish .= $converted_tree;
  1574.         }
  1575.         if (is_array($tree['right'])) {
  1576.             $converted_tree $this->toReversePolish($tree['right']);
  1577.             $polish .= $converted_tree;
  1578.         elseif ($tree['right'!= ''// It's a final node
  1579.             $converted_tree $this->_convert($tree['right']);
  1580.             $polish .= $converted_tree;
  1581.         }
  1582.         // if it's a function convert it here (so we can set it's arguments)
  1583.         if (preg_match("/^[A-Z0-9\xc0-\xdc\.]+$/",$tree['value']and
  1584.             !preg_match('/^([A-Ia-i]?[A-Za-z])(\d+)$/',$tree['value']and
  1585.             !preg_match("/^[A-Ia-i]?[A-Za-z](\d+)\.\.[A-Ia-i]?[A-Za-z](\d+)$/",$tree['value']and
  1586.             !is_numeric($tree['value']and
  1587.             !isset($this->ptg[$tree['value']]))
  1588.         {
  1589.             // left subtree for a function is always an array.
  1590.             if ($tree['left'!= ''{
  1591.                 $left_tree $this->toReversePolish($tree['left']);
  1592.             else {
  1593.                 $left_tree '';
  1594.             }
  1595.             // add it's left subtree and return.
  1596.             return $left_tree.$this->_convertFunction($tree['value']$tree['right']);
  1597.         else {
  1598.             $converted_tree $this->_convert($tree['value']);
  1599.         }
  1600.         $polish .= $converted_tree;
  1601.         return $polish;
  1602.     }
  1603.  
  1604. }

Documentation generated on Sun, 27 Feb 2011 16:33:09 -0800 by phpDocumentor 1.4.3