Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/lib/excel/Parser.php
Go to the documentation of this file.
00001 <?php
00028 define('ADD',"+");
00029 
00033 define('SUB',"-");
00034 
00038 define('EQUAL',"=");
00039 
00043 define('MUL',"*");
00044 
00048 define('DIV',"/");
00049 
00053 define('OPEN',"(");
00054 
00058 define('CLOSE',")");
00059 
00063 define('COMA',",");
00064 
00071 class Parser
00072   {
00079   function Parser($byte_order = 0)
00080     {
00081     $this->_current_char  = 0;        // The index of the character we are currently looking at.
00082     $this->_current_token = '';       // The token we are working on.
00083     $this->_formula       = "";       // The formula to parse.
00084     $this->_lookahead     = '';       // The character ahead of the current char.
00085     $this->_parse_tree    = '';       // The parse tree to be generated.
00086     $this->_initialize_hashes();      // Initialize the hashes: ptg's and function's ptg's
00087     $this->_byte_order = $byte_order; // Little Endian or Big Endian
00088     $this->_func_args  = 0;           // Number of arguments for the current function
00089     $this->_volatile   = 0;
00090     }
00091 
00095   function _initialize_hashes()
00096     {
00097     // The Excel ptg indices
00098     $this->ptg = array(
00099         'ptgExp'       => 0x01,
00100         'ptgTbl'       => 0x02,
00101         'ptgAdd'       => 0x03,
00102         'ptgSub'       => 0x04,
00103         'ptgMul'       => 0x05,
00104         'ptgDiv'       => 0x06,
00105         'ptgPower'     => 0x07,
00106         'ptgConcat'    => 0x08,
00107         'ptgLT'        => 0x09,
00108         'ptgLE'        => 0x0A,
00109         'ptgEQ'        => 0x0B,
00110         'ptgGE'        => 0x0C,
00111         'ptgGT'        => 0x0D,
00112         'ptgNE'        => 0x0E,
00113         'ptgIsect'     => 0x0F,
00114         'ptgUnion'     => 0x10,
00115         'ptgRange'     => 0x11,
00116         'ptgUplus'     => 0x12,
00117         'ptgUminus'    => 0x13,
00118         'ptgPercent'   => 0x14,
00119         'ptgParen'     => 0x15,
00120         'ptgMissArg'   => 0x16,
00121         'ptgStr'       => 0x17,
00122         'ptgAttr'      => 0x19,
00123         'ptgSheet'     => 0x1A,
00124         'ptgEndSheet'  => 0x1B,
00125         'ptgErr'       => 0x1C,
00126         'ptgBool'      => 0x1D,
00127         'ptgInt'       => 0x1E,
00128         'ptgNum'       => 0x1F,
00129         'ptgArray'     => 0x20,
00130         'ptgFunc'      => 0x21,
00131         'ptgFuncVar'   => 0x22,
00132         'ptgName'      => 0x23,
00133         'ptgRef'       => 0x24,
00134         'ptgArea'      => 0x25,
00135         'ptgMemArea'   => 0x26,
00136         'ptgMemErr'    => 0x27,
00137         'ptgMemNoMem'  => 0x28,
00138         'ptgMemFunc'   => 0x29,
00139         'ptgRefErr'    => 0x2A,
00140         'ptgAreaErr'   => 0x2B,
00141         'ptgRefN'      => 0x2C,
00142         'ptgAreaN'     => 0x2D,
00143         'ptgMemAreaN'  => 0x2E,
00144         'ptgMemNoMemN' => 0x2F,
00145         'ptgNameX'     => 0x39,
00146         'ptgRef3d'     => 0x3A,
00147         'ptgArea3d'    => 0x3B,
00148         'ptgRefErr3d'  => 0x3C,
00149         'ptgAreaErr3d' => 0x3D,
00150         'ptgArrayV'    => 0x40,
00151         'ptgFuncV'     => 0x41,
00152         'ptgFuncVarV'  => 0x42,
00153         'ptgNameV'     => 0x43,
00154         'ptgRefV'      => 0x44,
00155         'ptgAreaV'     => 0x45,
00156         'ptgMemAreaV'  => 0x46,
00157         'ptgMemErrV'   => 0x47,
00158         'ptgMemNoMemV' => 0x48,
00159         'ptgMemFuncV'  => 0x49,
00160         'ptgRefErrV'   => 0x4A,
00161         'ptgAreaErrV'  => 0x4B,
00162         'ptgRefNV'     => 0x4C,
00163         'ptgAreaNV'    => 0x4D,
00164         'ptgMemAreaNV' => 0x4E,
00165         'ptgMemNoMemN' => 0x4F,
00166         'ptgFuncCEV'   => 0x58,
00167         'ptgNameXV'    => 0x59,
00168         'ptgRef3dV'    => 0x5A,
00169         'ptgArea3dV'   => 0x5B,
00170         'ptgRefErr3dV' => 0x5C,
00171         'ptgAreaErr3d' => 0x5D,
00172         'ptgArrayA'    => 0x60,
00173         'ptgFuncA'     => 0x61,
00174         'ptgFuncVarA'  => 0x62,
00175         'ptgNameA'     => 0x63,
00176         'ptgRefA'      => 0x64,
00177         'ptgAreaA'     => 0x65,
00178         'ptgMemAreaA'  => 0x66,
00179         'ptgMemErrA'   => 0x67,
00180         'ptgMemNoMemA' => 0x68,
00181         'ptgMemFuncA'  => 0x69,
00182         'ptgRefErrA'   => 0x6A,
00183         'ptgAreaErrA'  => 0x6B,
00184         'ptgRefNA'     => 0x6C,
00185         'ptgAreaNA'    => 0x6D,
00186         'ptgMemAreaNA' => 0x6E,
00187         'ptgMemNoMemN' => 0x6F,
00188         'ptgFuncCEA'   => 0x78,
00189         'ptgNameXA'    => 0x79,
00190         'ptgRef3dA'    => 0x7A,
00191         'ptgArea3dA'   => 0x7B,
00192         'ptgRefErr3dA' => 0x7C,
00193         'ptgAreaErr3d' => 0x7D
00194         );
00195 
00196     // Thanks to Michael Meeks and Gnumeric for the initial arg values.
00197     //
00198     // The following hash was generated by "function_locale.pl" in the distro.
00199     // Refer to function_locale.pl for non-English function names.
00200     //
00201     // The array elements are as follow:
00202     // ptg:   The Excel function ptg code.
00203     // args:  The number of arguments that the function takes:
00204     //           >=0 is a fixed number of arguments.
00205     //           -1  is a variable  number of arguments.
00206     // class: The reference, value or array class of the function args.
00207     // vol:   The function is volatile.
00208     //
00209     $this->_functions = array(
00210           // function                  ptg  args  class  vol
00211           'COUNT'           => array(   0,   -1,    0,    0 ),
00212           'IF'              => array(   1,   -1,    1,    0 ),
00213           'ISNA'            => array(   2,    1,    1,    0 ),
00214           'ISERROR'         => array(   3,    1,    1,    0 ),
00215           'SUM'             => array(   4,   -1,    0,    0 ),
00216           'AVERAGE'         => array(   5,   -1,    0,    0 ),
00217           'MIN'             => array(   6,   -1,    0,    0 ),
00218           'MAX'             => array(   7,   -1,    0,    0 ),
00219           'ROW'             => array(   8,   -1,    0,    0 ),
00220           'COLUMN'          => array(   9,   -1,    0,    0 ),
00221           'NA'              => array(  10,    0,    0,    0 ),
00222           'NPV'             => array(  11,   -1,    1,    0 ),
00223           'STDEV'           => array(  12,   -1,    0,    0 ),
00224           'DOLLAR'          => array(  13,   -1,    1,    0 ),
00225           'FIXED'           => array(  14,   -1,    1,    0 ),
00226           'SIN'             => array(  15,    1,    1,    0 ),
00227           'COS'             => array(  16,    1,    1,    0 ),
00228           'TAN'             => array(  17,    1,    1,    0 ),
00229           'ATAN'            => array(  18,    1,    1,    0 ),
00230           'PI'              => array(  19,    0,    1,    0 ),
00231           'SQRT'            => array(  20,    1,    1,    0 ),
00232           'EXP'             => array(  21,    1,    1,    0 ),
00233           'LN'              => array(  22,    1,    1,    0 ),
00234           'LOG10'           => array(  23,    1,    1,    0 ),
00235           'ABS'             => array(  24,    1,    1,    0 ),
00236           'INT'             => array(  25,    1,    1,    0 ),
00237           'SIGN'            => array(  26,    1,    1,    0 ),
00238           'ROUND'           => array(  27,    2,    1,    0 ),
00239           'LOOKUP'          => array(  28,   -1,    0,    0 ),
00240           'INDEX'           => array(  29,   -1,    0,    1 ),
00241           'REPT'            => array(  30,    2,    1,    0 ),
00242           'MID'             => array(  31,    3,    1,    0 ),
00243           'LEN'             => array(  32,    1,    1,    0 ),
00244           'VALUE'           => array(  33,    1,    1,    0 ),
00245           'TRUE'            => array(  34,    0,    1,    0 ),
00246           'FALSE'           => array(  35,    0,    1,    0 ),
00247           'AND'             => array(  36,   -1,    0,    0 ),
00248           'OR'              => array(  37,   -1,    0,    0 ),
00249           'NOT'             => array(  38,    1,    1,    0 ),
00250           'MOD'             => array(  39,    2,    1,    0 ),
00251           'DCOUNT'          => array(  40,    3,    0,    0 ),
00252           'DSUM'            => array(  41,    3,    0,    0 ),
00253           'DAVERAGE'        => array(  42,    3,    0,    0 ),
00254           'DMIN'            => array(  43,    3,    0,    0 ),
00255           'DMAX'            => array(  44,    3,    0,    0 ),
00256           'DSTDEV'          => array(  45,    3,    0,    0 ),
00257           'VAR'             => array(  46,   -1,    0,    0 ),
00258           'DVAR'            => array(  47,    3,    0,    0 ),
00259           'TEXT'            => array(  48,    2,    1,    0 ),
00260           'LINEST'          => array(  49,   -1,    0,    0 ),
00261           'TREND'           => array(  50,   -1,    0,    0 ),
00262           'LOGEST'          => array(  51,   -1,    0,    0 ),
00263           'GROWTH'          => array(  52,   -1,    0,    0 ),
00264           'PV'              => array(  56,   -1,    1,    0 ),
00265           'FV'              => array(  57,   -1,    1,    0 ),
00266           'NPER'            => array(  58,   -1,    1,    0 ),
00267           'PMT'             => array(  59,   -1,    1,    0 ),
00268           'RATE'            => array(  60,   -1,    1,    0 ),
00269           'MIRR'            => array(  61,    3,    0,    0 ),
00270           'IRR'             => array(  62,   -1,    0,    0 ),
00271           'RAND'            => array(  63,    0,    1,    1 ),
00272           'MATCH'           => array(  64,   -1,    0,    0 ),
00273           'DATE'            => array(  65,    3,    1,    0 ),
00274           'TIME'            => array(  66,    3,    1,    0 ),
00275           'DAY'             => array(  67,    1,    1,    0 ),
00276           'MONTH'           => array(  68,    1,    1,    0 ),
00277           'YEAR'            => array(  69,    1,    1,    0 ),
00278           'WEEKDAY'         => array(  70,   -1,    1,    0 ),
00279           'HOUR'            => array(  71,    1,    1,    0 ),
00280           'MINUTE'          => array(  72,    1,    1,    0 ),
00281           'SECOND'          => array(  73,    1,    1,    0 ),
00282           'NOW'             => array(  74,    0,    1,    1 ),
00283           'AREAS'           => array(  75,    1,    0,    1 ),
00284           'ROWS'            => array(  76,    1,    0,    1 ),
00285           'COLUMNS'         => array(  77,    1,    0,    1 ),
00286           'OFFSET'          => array(  78,   -1,    0,    1 ),
00287           'SEARCH'          => array(  82,   -1,    1,    0 ),
00288           'TRANSPOSE'       => array(  83,    1,    1,    0 ),
00289           'TYPE'            => array(  86,    1,    1,    0 ),
00290           'ATAN2'           => array(  97,    2,    1,    0 ),
00291           'ASIN'            => array(  98,    1,    1,    0 ),
00292           'ACOS'            => array(  99,    1,    1,    0 ),
00293           'CHOOSE'          => array( 100,   -1,    1,    0 ),
00294           'HLOOKUP'         => array( 101,   -1,    0,    0 ),
00295           'VLOOKUP'         => array( 102,   -1,    0,    0 ),
00296           'ISREF'           => array( 105,    1,    0,    0 ),
00297           'LOG'             => array( 109,   -1,    1,    0 ),
00298           'CHAR'            => array( 111,    1,    1,    0 ),
00299           'LOWER'           => array( 112,    1,    1,    0 ),
00300           'UPPER'           => array( 113,    1,    1,    0 ),
00301           'PROPER'          => array( 114,    1,    1,    0 ),
00302           'LEFT'            => array( 115,   -1,    1,    0 ),
00303           'RIGHT'           => array( 116,   -1,    1,    0 ),
00304           'EXACT'           => array( 117,    2,    1,    0 ),
00305           'TRIM'            => array( 118,    1,    1,    0 ),
00306           'REPLACE'         => array( 119,    4,    1,    0 ),
00307           'SUBSTITUTE'      => array( 120,   -1,    1,    0 ),
00308           'CODE'            => array( 121,    1,    1,    0 ),
00309           'FIND'            => array( 124,   -1,    1,    0 ),
00310           'CELL'            => array( 125,   -1,    0,    1 ),
00311           'ISERR'           => array( 126,    1,    1,    0 ),
00312           'ISTEXT'          => array( 127,    1,    1,    0 ),
00313           'ISNUMBER'        => array( 128,    1,    1,    0 ),
00314           'ISBLANK'         => array( 129,    1,    1,    0 ),
00315           'T'               => array( 130,    1,    0,    0 ),
00316           'N'               => array( 131,    1,    0,    0 ),
00317           'DATEVALUE'       => array( 140,    1,    1,    0 ),
00318           'TIMEVALUE'       => array( 141,    1,    1,    0 ),
00319           'SLN'             => array( 142,    3,    1,    0 ),
00320           'SYD'             => array( 143,    4,    1,    0 ),
00321           'DDB'             => array( 144,   -1,    1,    0 ),
00322           'INDIRECT'        => array( 148,   -1,    1,    1 ),
00323           'CALL'            => array( 150,   -1,    1,    0 ),
00324           'CLEAN'           => array( 162,    1,    1,    0 ),
00325           'MDETERM'         => array( 163,    1,    2,    0 ),
00326           'MINVERSE'        => array( 164,    1,    2,    0 ),
00327           'MMULT'           => array( 165,    2,    2,    0 ),
00328           'IPMT'            => array( 167,   -1,    1,    0 ),
00329           'PPMT'            => array( 168,   -1,    1,    0 ),
00330           'COUNTA'          => array( 169,   -1,    0,    0 ),
00331           'PRODUCT'         => array( 183,   -1,    0,    0 ),
00332           'FACT'            => array( 184,    1,    1,    0 ),
00333           'DPRODUCT'        => array( 189,    3,    0,    0 ),
00334           'ISNONTEXT'       => array( 190,    1,    1,    0 ),
00335           'STDEVP'          => array( 193,   -1,    0,    0 ),
00336           'VARP'            => array( 194,   -1,    0,    0 ),
00337           'DSTDEVP'         => array( 195,    3,    0,    0 ),
00338           'DVARP'           => array( 196,    3,    0,    0 ),
00339           'TRUNC'           => array( 197,   -1,    1,    0 ),
00340           'ISLOGICAL'       => array( 198,    1,    1,    0 ),
00341           'DCOUNTA'         => array( 199,    3,    0,    0 ),
00342           'ROUNDUP'         => array( 212,    2,    1,    0 ),
00343           'ROUNDDOWN'       => array( 213,    2,    1,    0 ),
00344           'RANK'            => array( 216,   -1,    0,    0 ),
00345           'ADDRESS'         => array( 219,   -1,    1,    0 ),
00346           'DAYS360'         => array( 220,   -1,    1,    0 ),
00347           'TODAY'           => array( 221,    0,    1,    1 ),
00348           'VDB'             => array( 222,   -1,    1,    0 ),
00349           'MEDIAN'          => array( 227,   -1,    0,    0 ),
00350           'SUMPRODUCT'      => array( 228,   -1,    2,    0 ),
00351           'SINH'            => array( 229,    1,    1,    0 ),
00352           'COSH'            => array( 230,    1,    1,    0 ),
00353           'TANH'            => array( 231,    1,    1,    0 ),
00354           'ASINH'           => array( 232,    1,    1,    0 ),
00355           'ACOSH'           => array( 233,    1,    1,    0 ),
00356           'ATANH'           => array( 234,    1,    1,    0 ),
00357           'DGET'            => array( 235,    3,    0,    0 ),
00358           'INFO'            => array( 244,    1,    1,    1 ),
00359           'DB'              => array( 247,   -1,    1,    0 ),
00360           'FREQUENCY'       => array( 252,    2,    0,    0 ),
00361           'ERROR.TYPE'      => array( 261,    1,    1,    0 ),
00362           'REGISTER.ID'     => array( 267,   -1,    1,    0 ),
00363           'AVEDEV'          => array( 269,   -1,    0,    0 ),
00364           'BETADIST'        => array( 270,   -1,    1,    0 ),
00365           'GAMMALN'         => array( 271,    1,    1,    0 ),
00366           'BETAINV'         => array( 272,   -1,    1,    0 ),
00367           'BINOMDIST'       => array( 273,    4,    1,    0 ),
00368           'CHIDIST'         => array( 274,    2,    1,    0 ),
00369           'CHIINV'          => array( 275,    2,    1,    0 ),
00370           'COMBIN'          => array( 276,    2,    1,    0 ),
00371           'CONFIDENCE'      => array( 277,    3,    1,    0 ),
00372           'CRITBINOM'       => array( 278,    3,    1,    0 ),
00373           'EVEN'            => array( 279,    1,    1,    0 ),
00374           'EXPONDIST'       => array( 280,    3,    1,    0 ),
00375           'FDIST'           => array( 281,    3,    1,    0 ),
00376           'FINV'            => array( 282,    3,    1,    0 ),
00377           'FISHER'          => array( 283,    1,    1,    0 ),
00378           'FISHERINV'       => array( 284,    1,    1,    0 ),
00379           'FLOOR'           => array( 285,    2,    1,    0 ),
00380           'GAMMADIST'       => array( 286,    4,    1,    0 ),
00381           'GAMMAINV'        => array( 287,    3,    1,    0 ),
00382           'CEILING'         => array( 288,    2,    1,    0 ),
00383           'HYPGEOMDIST'     => array( 289,    4,    1,    0 ),
00384           'LOGNORMDIST'     => array( 290,    3,    1,    0 ),
00385           'LOGINV'          => array( 291,    3,    1,    0 ),
00386           'NEGBINOMDIST'    => array( 292,    3,    1,    0 ),
00387           'NORMDIST'        => array( 293,    4,    1,    0 ),
00388           'NORMSDIST'       => array( 294,    1,    1,    0 ),
00389           'NORMINV'         => array( 295,    3,    1,    0 ),
00390           'NORMSINV'        => array( 296,    1,    1,    0 ),
00391           'STANDARDIZE'     => array( 297,    3,    1,    0 ),
00392           'ODD'             => array( 298,    1,    1,    0 ),
00393           'PERMUT'          => array( 299,    2,    1,    0 ),
00394           'POISSON'         => array( 300,    3,    1,    0 ),
00395           'TDIST'           => array( 301,    3,    1,    0 ),
00396           'WEIBULL'         => array( 302,    4,    1,    0 ),
00397           'SUMXMY2'         => array( 303,    2,    2,    0 ),
00398           'SUMX2MY2'        => array( 304,    2,    2,    0 ),
00399           'SUMX2PY2'        => array( 305,    2,    2,    0 ),
00400           'CHITEST'         => array( 306,    2,    2,    0 ),
00401           'CORREL'          => array( 307,    2,    2,    0 ),
00402           'COVAR'           => array( 308,    2,    2,    0 ),
00403           'FORECAST'        => array( 309,    3,    2,    0 ),
00404           'FTEST'           => array( 310,    2,    2,    0 ),
00405           'INTERCEPT'       => array( 311,    2,    2,    0 ),
00406           'PEARSON'         => array( 312,    2,    2,    0 ),
00407           'RSQ'             => array( 313,    2,    2,    0 ),
00408           'STEYX'           => array( 314,    2,    2,    0 ),
00409           'SLOPE'           => array( 315,    2,    2,    0 ),
00410           'TTEST'           => array( 316,    4,    2,    0 ),
00411           'PROB'            => array( 317,   -1,    2,    0 ),
00412           'DEVSQ'           => array( 318,   -1,    0,    0 ),
00413           'GEOMEAN'         => array( 319,   -1,    0,    0 ),
00414           'HARMEAN'         => array( 320,   -1,    0,    0 ),
00415           'SUMSQ'           => array( 321,   -1,    0,    0 ),
00416           'KURT'            => array( 322,   -1,    0,    0 ),
00417           'SKEW'            => array( 323,   -1,    0,    0 ),
00418           'ZTEST'           => array( 324,   -1,    0,    0 ),
00419           'LARGE'           => array( 325,    2,    0,    0 ),
00420           'SMALL'           => array( 326,    2,    0,    0 ),
00421           'QUARTILE'        => array( 327,    2,    0,    0 ),
00422           'PERCENTILE'      => array( 328,    2,    0,    0 ),
00423           'PERCENTRANK'     => array( 329,   -1,    0,    0 ),
00424           'MODE'            => array( 330,   -1,    2,    0 ),
00425           'TRIMMEAN'        => array( 331,    2,    0,    0 ),
00426           'TINV'            => array( 332,    2,    1,    0 ),
00427           'CONCATENATE'     => array( 336,   -1,    1,    0 ),
00428           'POWER'           => array( 337,    2,    1,    0 ),
00429           'RADIANS'         => array( 342,    1,    1,    0 ),
00430           'DEGREES'         => array( 343,    1,    1,    0 ),
00431           'SUBTOTAL'        => array( 344,   -1,    0,    0 ),
00432           'SUMIF'           => array( 345,   -1,    0,    0 ),
00433           'COUNTIF'         => array( 346,    2,    0,    0 ),
00434           'COUNTBLANK'      => array( 347,    1,    0,    0 ),
00435           'ROMAN'           => array( 354,   -1,    1,    0 )
00436           );
00437     }
00438 
00444   function _convert($token)
00445     {
00446     if(is_numeric($token))
00447         {
00448         return($this->_convert_number($token));
00449         }
00450     // match references like A1
00451     elseif(preg_match("/^([A-I]?[A-Z])(\d+)$/",$token))
00452         {
00453         return($this->_convert_ref2d($token));
00454         }
00455     // match ranges like A1:B2
00456     elseif(preg_match("/^([A-I]?[A-Z])(\d+)\:([A-I]?[A-Z])(\d+)$/",$token))
00457         {
00458         return($this->_convert_range2d($token));
00459         }
00460     // match ranges like A1..B2
00461     elseif(preg_match("/^([A-I]?[A-Z])(\d+)\.\.([A-I]?[A-Z])(\d+)$/",$token))
00462         {
00463         return($this->_convert_range2d($token));
00464         }
00465     elseif(isset($this->ptg[$token])) // operators (including parentheses)
00466         {
00467         return(pack("C", $this->ptg[$token]));
00468         }
00469     elseif(preg_match("/[A-Z0-9�-�\.]+/",$token))
00470         {
00471         return($this->_convert_function($token,$this->_func_args));
00472         }
00473     // if it's an argument, ignore the token (the argument remains)
00474     elseif($token == 'arg')
00475         {
00476         $this->_func_args++;
00477         return('');
00478         }
00479     die("Unknown token $token");
00480     }
00481 
00487   function _convert_number($num)
00488     {
00489     // Integer in the range 0..2**16-1
00490     if ((preg_match("/^\d+$/",$num)) and ($num <= 65535)) {
00491         return pack("Cv", $this->ptg['ptgInt'], $num);
00492         }
00493     else // A float
00494         {
00495         if($this->_byte_order) // if it's Big Endian
00496             {
00497             $num = strrev($num);
00498             }
00499         return pack("Cd", $this->ptg['ptgNum'], $num);
00500         }
00501     }
00502 
00510   function _convert_function($token, $num_args)
00511     {
00512     $this->_func_args = 0; // re initialize the number of arguments
00513     $args     = $this->_functions[$token][1];
00514     $volatile = $this->_functions[$token][3];
00515 
00516     if($volatile) {
00517         $this->_volatile = 1;
00518         }
00519     // Fixed number of args eg. TIME($i,$j,$k).
00520     if ($args >= 0)
00521         {
00522         return(pack("Cv", $this->ptg['ptgFuncV'], $this->_functions[$token][0]));
00523         }
00524     // Variable number of args eg. SUM($i,$j,$k, ..).
00525     if ($args == -1) {
00526         return(pack("CCv", $this->ptg['ptgFuncVarV'], $num_args, $this->_functions[$token][0]));
00527         }
00528     }
00529 
00535   function _convert_range2d($range)
00536     {
00537     $class = 2; // as far as I know, this is magick.
00538 
00539     // Split the range into 2 cell refs
00540     if(preg_match("/^([A-I]?[A-Z])(\d+)\:([A-I]?[A-Z])(\d+)$/",$range)) {
00541         list($cell1, $cell2) = explode(':', $range);
00542         }
00543     elseif(preg_match("/^([A-I]?[A-Z])(\d+)\.\.([A-I]?[A-Z])(\d+)$/",$range)) {
00544         list($cell1, $cell2) = explode('..', $range);
00545         }
00546     else {
00547         die("Unknown range separator");
00548         }
00549 
00550     // Convert the cell references
00551     list($row1, $col1) = $this->_cell_to_packed_rowcol($cell1);
00552     list($row2, $col2) = $this->_cell_to_packed_rowcol($cell2);
00553 
00554     // The ptg value depends on the class of the ptg.
00555     if ($class == 0) {
00556         $ptgArea = pack("C", $this->ptg['ptgArea']);
00557         }
00558     elseif ($class == 1) {
00559         $ptgArea = pack("C", $this->ptg['ptgAreaV']);
00560         }
00561     elseif ($class == 2) {
00562         $ptgArea = pack("C", $this->ptg['ptgAreaA']);
00563         }
00564     else{
00565         die("Unknown class ");
00566         }
00567 
00568     return($ptgArea . $row1 . $row2 . $col1. $col2);
00569     }
00570 
00576   function _convert_ref2d($cell)
00577     {
00578     $class = 2; // as far as I know, this is magick.
00579 
00580     // Convert the cell reference
00581     list($row, $col) = $this->_cell_to_packed_rowcol($cell);
00582 
00583     // The ptg value depends on the class of the ptg.
00584     if ($class == 0) {
00585         $ptgRef = pack("C", $this->ptg['ptgRef']);
00586         }
00587     elseif ($class == 1) {
00588         $ptgRef = pack("C", $this->ptg['ptgRefV']);
00589         }
00590     elseif ($class == 2) {
00591         $ptgRef = pack("C", $this->ptg['ptgRefA']);
00592         }
00593     else{
00594         die("Unknown class ");
00595         }
00596     return $ptgRef.$row.$col;
00597     }
00598 
00604   function _cell_to_packed_rowcol($cell)
00605     {
00606     list($row, $col, $row_rel, $col_rel) = $this->_cell_to_rowcol($cell);
00607     if ($col >= 256) {
00608         die("Column in: $cell greater than 255 ");
00609         }
00610     if ($row >= 16384) {
00611         die("Row in: $cell greater than 16384 ");
00612         }
00613 
00614     // Set the high bits to indicate if row or col are relative.
00615     $row    |= $col_rel << 14;
00616     $row    |= $row_rel << 15;
00617 
00618     $row     = pack('v', $row);
00619     $col     = pack('C', $col);
00620 
00621     return (array($row, $col));
00622     }
00623 
00631   function _cell_to_rowcol($cell)
00632     {
00633     preg_match('/(\$)?([A-I]?[A-Z])(\$)?(\d+)/',$cell,$match);
00634     // return absolute column if there is a $ in the ref
00635     $col_rel = empty($match[1]) ? 1 : 0;
00636     $col_ref = $match[2];
00637     $row_rel = empty($match[3]) ? 1 : 0;
00638     $row     = $match[4];
00639 
00640     // Convert base26 column string to a number.
00641     $expn   = strlen($col_ref) - 1;
00642     $col    = 0;
00643     for($i=0; $i < strlen($col_ref); $i++)
00644     {
00645         $col += (ord($col_ref{$i}) - ord('A') + 1) * pow(26, $expn);
00646         $expn--;
00647     }
00648 
00649     // Convert 1-index to zero-index
00650     $row--;
00651     $col--;
00652 
00653     return(array($row, $col, $row_rel, $col_rel));
00654     }
00655 
00659   function _advance()
00660     {
00661     $i = $this->_current_char;
00662     // eat up white spaces
00663     if($i < strlen($this->_formula))
00664         {
00665         while($this->_formula{$i} == " ")
00666             {
00667             $i++;
00668             }
00669         if($i < strlen($this->_formula) - 1)
00670             {
00671             $this->_lookahead = $this->_formula{$i+1};
00672             }
00673         $token = "";
00674         }
00675     while($i < strlen($this->_formula))
00676         {
00677         $token .= $this->_formula{$i};
00678         if($this->_match($token) != '')
00679             {
00680             if($i < strlen($this->_formula) - 1)
00681                 {
00682                 $this->_lookahead = $this->_formula{$i+1};
00683                 }
00684             $this->_current_char = $i + 1;
00685             $this->_current_token = $token;
00686             return(1);
00687             }
00688         $this->_lookahead = $this->_formula{$i+2};
00689         $i++;
00690         }
00691     //die("Lexical error ".$this->_current_char);
00692     }
00693 
00699   function _match($token)
00700     {
00701     switch($token)
00702         {
00703         case ADD:
00704             return($token);
00705             break;
00706         case SUB:
00707             return($token);
00708             break;
00709         case MUL:
00710             return($token);
00711             break;
00712         case DIV:
00713             return($token);
00714             break;
00715         case OPEN:
00716             return($token);
00717             break;
00718         case CLOSE:
00719             return($token);
00720             break;
00721         case COMA:
00722             return($token);
00723             break;
00724         default:
00725             // if it's a reference
00726             if(preg_match("/^[A-I]?[A-Z][0-9]+$/i",$token) and
00727                !preg_match("/[0-9]/",$this->_lookahead) and
00728                ($this->_lookahead != ':') and ($this->_lookahead != '.'))
00729                 {
00730                 return($token);
00731                 }
00732             // if it's a range (A1:A2)
00733             elseif(preg_match("/^[A-I]?[A-Z][0-9]+:[A-I]?[A-Z][0-9]+$/i",$token) and
00734                    !preg_match("/[0-9]/",$this->_lookahead))
00735                 {
00736                 return($token);
00737                 }
00738             // if it's a range (A1..A2)
00739             elseif(preg_match("/^[A-I]?[A-Z][0-9]+\.\.[A-I]?[A-Z][0-9]+$/i",$token) and
00740                    !preg_match("/[0-9]/",$this->_lookahead))
00741                 {
00742                 return($token);
00743                 }
00744             elseif(is_numeric($token) and !is_numeric($token.$this->_lookahead))
00745                 {
00746                 return($token);
00747                 }
00748             // if it's a function call
00749             elseif(preg_match("/^[A-Z0-9�-�\.]+$/i",$token) and ($this->_lookahead == "("))
00750 
00751                 {
00752                 return($token);
00753                 }
00754             return '';
00755         }
00756     }
00757 
00764   function parse($formula)
00765     {
00766     $this->_current_char = 0;
00767     $this->_formula      = $formula;
00768     $this->_lookahead    = $formula{1};
00769     $this->_advance();
00770     $this->_parse_tree   = $this->_expression();
00771     }
00772 
00779   function _expression()
00780     {
00781     $result = $this->_term();
00782     while ($this->_current_token == ADD or $this->_current_token == SUB)
00783         {
00784         if ($this->_current_token == ADD)
00785             {
00786             $this->_advance();
00787             $result = $this->_create_tree('ptgAdd', $result, $this->_term());
00788             }
00789         else 
00790             {
00791             $this->_advance();
00792             $result = $this->_create_tree('ptgSub', $result, $this->_term());
00793             }
00794         }
00795     return $result;
00796     }
00797 
00805   function _parenthesized_expression()
00806     {
00807     $result = $this->_create_tree('ptgParen', $this->_expression(), '');
00808     return($result);
00809     }
00810 
00817   function _term()
00818     {
00819     $result = $this->_fact();
00820     while ($this->_current_token == MUL || $this->_current_token == DIV)
00821         {
00822         if ($this->_current_token == MUL)
00823             {
00824             $this->_advance();
00825             $result = $this->_create_tree('ptgMul', $result, $this->_fact());
00826             }
00827         else 
00828             {
00829             $this->_advance();
00830             $result = $this->_create_tree('ptgDiv', $result, $this->_fact());
00831             }
00832         }
00833     return($result);
00834     }
00835 
00846   function _fact()
00847     {
00848     if ($this->_current_token == OPEN)
00849         {
00850         $this->_advance();         // eat the "("
00851         $result = $this->_parenthesized_expression();//$this->_expression();
00852 
00853         if ($this->_current_token != CLOSE) {
00854             die("')' token expected.");
00855             }
00856         $this->_advance();         // eat the ")"
00857         return($result);
00858         }
00859     // if it's a reference
00860     if (preg_match("/^[A-I]?[A-Z][0-9]+$/i",$this->_current_token))
00861         {
00862         $result = $this->_create_tree($this->_current_token, '', '');
00863         $this->_advance();
00864         return($result);
00865         }
00866     // if it's a range
00867     elseif (preg_match("/^[A-I]?[A-Z][0-9]+:[A-I]?[A-Z][0-9]+$/i",$this->_current_token) or
00868             preg_match("/^[A-I]?[A-Z][0-9]+\.\.[A-I]?[A-Z][0-9]+$/i",$this->_current_token))
00869         {
00870         $result = $this->_current_token;
00871         $this->_advance();
00872         return($result);
00873         }
00874     elseif (is_numeric($this->_current_token))
00875         {
00876         $result = $this->_create_tree($this->_current_token, '', '');
00877         $this->_advance();
00878         return($result);
00879         }
00880     // if it's a function call
00881     elseif (preg_match("/^[A-Z0-9�-�\.]+$/i",$this->_current_token))
00882         {
00883         $result = $this->_func();
00884         return($result);
00885         }
00886     die("Sintactic error: ".$this->_current_token.", lookahead: ".
00887         $this->_lookahead.", current char: ".$this->_current_char);
00888     }
00889 
00895   function _func()
00896     {
00897     $num_args = 0; // number of arguments received
00898     $function = $this->_current_token;
00899     $this->_advance();
00900     $this->_advance();         // eat the "("
00901     while($this->_current_token != ')')
00902         {
00903         if($num_args > 0)
00904             {
00905             if($this->_current_token == COMA) {
00906                 $this->_advance();  // eat the ","
00907                 }
00908             else {
00909                 die("Sintactic error: coma expected $num_args");
00910                 }
00911             $result = $this->_create_tree('arg', $result, $this->_expression());
00912             }
00913         else {
00914             $result = $this->_create_tree('arg', '', $this->_expression());
00915             }
00916         $num_args++;
00917         }
00918     $args = $this->_functions[$function][1];
00919     // If fixed number of args eg. TIME($i,$j,$k). Check that the number of args is valid.
00920     if (($args >= 0) and ($args != $num_args))
00921         {
00922         die("Incorrect number of arguments in function $function() ");
00923         }
00924 
00925     $result = $this->_create_tree($function, $result, '');
00926     $this->_advance();         // eat the ")"
00927     return($result);
00928     }
00929 
00938   function _create_tree($value, $left, $right)
00939     {
00940     return array('value' => $value, 'left' => $left, 'right' => $right);
00941     }
00942 
00969   function to_reverse_polish($tree = array())
00970     {
00971     $polish = ""; // the string we are going to return
00972     if (empty($tree)) // If it's the first call use _parse_tree
00973         {
00974         $tree = $this->_parse_tree;
00975         }
00976     if (is_array($tree['left']))
00977         {
00978         $polish .= $this->to_reverse_polish($tree['left']);
00979         }
00980     elseif($tree['left'] != '') // It's a final node
00981         {
00982         $polish .= $this->_convert($tree['left']); //$tree['left'];
00983         }
00984     if (is_array($tree['right']))
00985         {
00986         $polish .= $this->to_reverse_polish($tree['right']);
00987         }
00988     elseif($tree['right'] != '') // It's a final node
00989         {
00990         $polish .= $this->_convert($tree['right']);
00991         }
00992     $polish .= $this->_convert($tree['value']);
00993     return $polish;
00994     }
00995   }
00996 ?>
 All Data Structures Namespaces Files Functions Variables Enumerations