]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/ASCIIMathPHP/ASCIIMathPHP-2.0.class.php
Add svn:keywords
[SourceForge/phpwiki.git] / lib / ASCIIMathPHP / ASCIIMathPHP-2.0.class.php
1 <?php // $Id$
2
3 /****
4  * ASCIIMathPHP and associated classes:
5  * -- XMLNode
6  * -- MathMLNode extends XMLNode
7  *
8  * These classes are a PHP port of ASCIIMath 
9  * Version 1.3 Feb 19 2004, (c) Peter Jipsen http://www.chapman.edu/~jipsen
10  *
11  * ASCIIMathPHP Version 1.11, 26 April 2006, (c) Kee-Lin Steven Chan (kc56@cornell.edu)
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or (at
16  * your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful, 
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  * General Public License (at http://www.gnu.org/copyleft/gpl.html) 
22  * for more details.
23  *
24  * ChangeLog
25  *
26  * Ver 2.0
27  * -- PHP5 only version of ASCIIMathPHP
28  *
29  * Ver 1.12.1
30  * -- Included the missing setCurrExpr() method
31  *
32  * Ver 1.12
33  * -- Added changes that David Lippman <DLippman@pierce.ctc.edu> made to bring ASCIIMathPHP up to 
34  * ASCIIMath 1.4.7 functionality.
35  * -- Added parseIntExpr, for intermediate expression parsing rule, allowing x^2/x^3 to render as (x^2)/(x^3)
36  * -- Added quotes as another way of designating text; "hello" is equivalent to text(hello)
37  * -- Added FUNC designator to allow sin, cos, etc to act as functions, so sin(x)/x renders as {sin(x)}/x
38  *
39  * Ver 1.11
40  * -- Fixed bug that stopped script execution for incomplete expressions
41  * -- Changed the algorithm for parsing expressions so that it matches the longest string possible (greedy)
42  *
43  * Ver 1.10
44  * -- Added definition support
45  * -- Added stackrel support
46  * -- Added a bunch of different symbols etc. >>, << and definitions like dx, dy, dz etc.
47  *
48  * Ver 1.02
49  * -- Fixed bug with mbox and text
50  * -- Fixed spacing bug with mbox and text
51  *
52  * Ver 1.01
53  * -- Fixed Bug that did not parse symbols greater than a single character
54  * correctly when appearing at end of expression.
55  *
56  ***/
57
58 class XMLNode
59 {
60         // Private variables
61         var $_id;
62         var $_name;
63         var $_content;
64         var $_mt_elem_flg;
65         var $_attr_arr;
66         var $_child_arr;
67         var $_nmspc;
68         var $_nmspc_alias;
69         var $_parent_id;
70         var $_parent_node;
71         
72         function XMLNode($id = NULL)
73         {
74                 $this->_id = isset($id) ? $id : md5(uniqid(rand(),1));
75                 $this->_name = '';
76                 $this->_content = '';
77                 $this->_mt_elem_flg = FALSE;
78                 $this->_attr_arr = array();
79                 $this->_child_arr = array();
80                 $this->_nmspc = '';
81                 $this->_nmspc_alias = '';
82                 $this->_parent_id = FALSE;
83                 $this->_parent_node = NULL;
84         }
85         
86         function addChild(&$node)
87         {
88                 $this->_child_arr[$node->getId()] = $node;
89                 $node->setParentId($this->_id);
90                 $node->setParentNode($this);
91         }
92         
93         function addChildArr(&$node_arr)
94         {
95                 $key_arr = array_keys($node_arr);
96                 $num_key = count($key_arr);
97                 
98                 for ($i = 0; $i < $num_key; $i++) {
99                         $node = $node_arr[$key_arr[$i]];
100                         $this->addChild($node);
101                 }
102         }
103         
104         function insertChildBefore($idx,&$node)
105         {
106                 $key_arr = array_keys($this->_child_arr);
107                 $num_key = count($key_arr);
108                 $tmp_arr = arry();
109                 
110                 for ($i = 0;$i < $num_key;$i++) {
111                         if ($i == $idx) {
112                                 $tmp_arr[$node->getId()] = $node;
113                         }
114                         $tmp_arr[$key_arr[$i]] = $this->_child_arr[$key_arr[$i]];
115                 }
116                 $this->_child_arr = $tmp_arr;
117         }
118         
119         function insertChildAfter($idx,&$node)
120         {
121                 $key_arr = array_keys($this->_child_arr);
122                 $num_key = count($key_arr);
123                 $tmp_arr = arry();
124                 
125                 for ($i = 0;$i < $num_key;$i++) {
126                         $tmp_arr[$key_arr[$i]] = $this->_child_arr[$key_arr[$i]];
127                         if ($i == $idx) {
128                                 $tmp_arr[$node->getId()] = $node;
129                         }
130                 }
131                 $this->_child_arr = $tmp_arr;
132         }
133         
134         function setId($id)
135         {
136                 $this->_id = $id;
137         }
138         
139         function setName($name)
140         {
141                 $this->_name = $name;
142         }
143         
144         function setNamepace($nmspc)
145         {
146                 $this->_nmspc = $nmspc;
147         }
148         
149         function setNamespaceAlias($nmspc_alias)
150         {
151                 $this->_nmspc_alias = $nmspc_alias;
152         }
153         
154         function setContent($content)
155         {
156                 $this->_content = $content;
157         }
158         
159         function setEmptyElem($mt_elem_flg)
160         {
161                 $this->_mt_elem_flg = $mt_elem_flg;
162         }
163         
164         function setAttr($attr_nm,$attr_val)
165         {
166                 $this->_attr_arr[$attr_nm] = $attr_val;
167         }
168         
169         function setAttrArr($attr_arr)
170         {
171                 $this->_attr_arr = $attr_arr;
172         }
173         
174         function setParentId($id)
175         {
176                 $this->_parent_id = $id;
177         }
178         
179         function setParentNode(&$node)
180         {
181                 $this->_parent_node = $node;
182         }
183         
184         function getId()
185         {
186                 return($this->_id);
187         }
188         
189         function getName()
190         {
191                 return($this->_name);
192         }
193         
194         function getNamespace()
195         {
196                 return($this->_nmspc);
197         }
198         
199         function getNamespaceAlias()
200         {
201                 return($this->_nmspc_alias);
202         }
203         
204         function getContent()
205         {
206                 return($this->_content);
207         }
208         
209         function getAttr($attr_nm)
210         {
211                 if (isset($this->_attr_arr[$attr_nm])) {
212                         return($this->_attr_arr[$attr_nm]);
213                 } else {
214                         return(NULL);
215                 }
216         }
217         
218         function getAttrArr()
219         {
220                 return($this->_attr_arr);
221         }
222         
223         function getParentId()
224         {
225                 return($this->parent_id);
226         }
227         
228         function getParentNode()
229         {
230                 return($this->_parent_node);
231         }
232         
233         function getChild($id)
234         {
235                 if (isset($this->_child_arr[$id])) {
236                         return($this->_child_arr[$id]);
237                 } else {
238                         return(FALSE);
239                 }
240         }
241         
242         function getFirstChild()
243         {
244                 $id_arr = array_keys($this->_child_arr);
245                 $num_child = count($id_arr);
246                 
247                 if ($num_child > 0) {
248                         return($this->_child_arr[$id_arr[0]]);
249                 } else {
250                         return(FALSE);
251                 }
252         }
253         
254         function getLastChild()
255         {
256                 $id_arr = array_keys($this->_child_arr);
257                 $num_child = count($id_arr);
258                 
259                 if ($num_child > 0) {
260                         return($this->_child_arr[$id_arr[$num_child - 1]]);
261                 } else {
262                         return(FALSE);
263                 }
264         }
265         
266         function getChildByIdx($idx)
267         {
268                 $id_arr = array_keys($this->_child_arr);
269                 
270                 if (isset($this->_child_arr[$id_arr[$idx]])) {
271                         return($this->_child_arr[$id_arr[$idx]]);
272                 } else {
273                         return(FALSE);
274                 }
275         }
276         
277         function getNumChild()
278         {
279                 return(count($this->_child_arr));
280         }
281         
282         function removeChild($id)
283         {
284                 unset($this->_child_arr[$id]);
285         }
286         
287         function removeChildByIdx($idx)
288         {
289                 $key_arr = array_keys($this->_child_arr);
290                 unset($this->_child_arr[$key_arr[$idx]]);
291         }
292         
293         function removeFirstChild()
294         {
295                 $key_arr = array_keys($this->_child_arr);
296                 unset($this->_child_arr[$key_arr[0]]);
297         }
298         
299         function removeLastChild()
300         {
301                 $key_arr = array_keys($this->_child_arr);
302                 unset($this->_child_arr[$key_arr[count($key_arr)-1]]);
303         }
304         
305         function dumpXML($indent_str = "\t")
306         {
307                 $attr_txt = $this->_dumpAttr();
308                 $name = $this->_dumpName();
309                 $xmlns = $this->_dumpXmlns();
310                 $lvl = $this->_getCurrentLevel();
311                 $indent = str_pad('',$lvl,$indent_str);
312                 
313                 if ($this->_mt_elem_flg) {
314                         $tag = "$indent<$name$xmlns$attr_txt />";
315                         return($tag);
316                 } else {
317                         $key_arr = array_keys($this->_child_arr);
318                         $num_child = count($key_arr);
319                         
320                         $tag = "$indent<$name$xmlns$attr_txt>$this->_content";
321                         
322                         for ($i = 0;$i < $num_child;$i++) {
323                                 $node = $this->_child_arr[$key_arr[$i]];
324                                 
325                                 $child_txt = $node->dumpXML($indent_str);
326                                 $tag .= "\n$child_txt";
327                         }
328                         
329                         $tag .= ($num_child > 0 ? "\n$indent</$name>" : "</$name>");
330                         return($tag);
331                 }
332         }
333         
334         function _dumpAttr()
335         {
336                 $id_arr = array_keys($this->_attr_arr);
337                 $id_arr_cnt = count($id_arr);
338                 $attr_txt = '';
339                 
340                 for($i = 0;$i < $id_arr_cnt;$i++) {
341                         $key = $id_arr[$i];
342                         $attr_txt .= " $key=\"{$this->_attr_arr[$key]}\"";
343                 }
344                 
345                 return($attr_txt);
346         }
347         
348         function _dumpName()
349         {
350                 $alias = $this->getNamespaceAlias();
351                 if ($alias == '') {
352                         return($this->getName());
353                 } else {
354                         return("$alias:" . $this->getName());
355                 }
356         }
357         
358         function _dumpXmlns()
359         {
360                 $nmspc = $this->getNamespace();
361                 $alias = $this->getNamespaceAlias();
362                 
363                 if ($nmspc != '') {
364                         if ($alias == '') {
365                                 return(" xmlns=\"" . $nmspc . "\"");
366                         } else {
367                                 return(" xmlns:$alias=\"" . $nmspc . "\"");
368                         }
369                 } else {
370                         return('');
371                 }
372         }
373         
374         function _getCurrentLevel()
375         {
376                 if ($this->_parent_id === FALSE) {
377                         return(0);
378                 } else {
379                         $node = $this->getParentNode();
380                         $lvl = $node->_getCurrentLevel();
381                         $lvl++;
382                         return($lvl);
383                 }
384         }
385 }
386
387 class MathMLNode extends XMLNode
388 {
389         function MathMLNode($id = NULL)
390         {
391                 parent::XMLNode($id);
392         }
393         
394         function removeBrackets()
395         {
396                 if ($this->_name == 'mrow') {
397                         if ($c_node_0 = $this->getFirstChild()) {
398                                 $c_node_0->isLeftBracket() ? $this->removeFirstChild() : 0;
399                         }
400                         
401                         if ($c_node_0 = $this->getLastChild()) {
402                                 $c_node_0->isRightBracket() ? $this->removeLastChild() : 0;
403                         }
404                 }
405         }
406         
407         function isLeftBracket()
408         {
409                 switch ($this->_content) {
410                         case '{':
411                         case '[':
412                         case '(':
413                                 return(TRUE);
414                                 break;
415                 }
416                 return(FALSE);
417         }
418         
419         function isRightBracket()
420         {
421                 switch ($this->_content) {
422                         case '}':
423                         case ']':
424                         case ')':
425                                 return(TRUE);
426                                 break;
427                 }
428                 return(FALSE);
429         }
430 }
431
432 class ASCIIMathPHP
433 {
434         var $_expr;
435         var $_curr_expr;
436         var $_prev_expr;
437         var $_symbol_arr;
438         var $_node_arr;
439         var $_node_cntr;
440         
441         function ASCIIMathPHP($symbol_arr,$expr = NULL)
442         {
443                 $this->_symbol_arr = $symbol_arr;
444                 if (isset($expr)) {
445                         $this->setExpr($expr);
446                 }
447         }
448         
449         /**
450          * Returns an empty node (containing a non-breaking space) 26-Apr-2006
451          * 
452          * Used when an expression is incomplete
453          *
454          * @return object
455          *
456          * @access private
457          */
458         function emptyNode()
459         {
460                 $tmp_node = $this->createNode();
461                 $tmp_node->setName('mn');
462                 $tmp_node->setContent('&#' . hexdec('200B') . ';');
463                 return $tmp_node;
464         }
465         
466         function pushExpr($prefix) // 2005-06-11 wes
467         {
468                 $this->_curr_expr = $prefix . $this->_curr_expr;
469         }
470
471         function setExpr($expr)
472         {
473                 $this->_expr = $expr;
474                 $this->_curr_expr = $expr;
475                 $this->_prev_expr = $expr;
476                 
477                 $this->_node_arr = array();
478                 $this->_node_cntr = 0;
479         }
480         
481         function genMathML($attr_arr = NULL)
482         {
483                 // <math> node
484                 $node_0 = $this->createNode();
485                 $node_0->setName('math');
486                 $node_0->setNamepace('http://www.w3.org/1998/Math/MathML');
487                 
488                 // <mstyle> node
489                 if (isset($attr_arr)) {
490                         $node_1 = $this->createNode();
491                         $node_1->setName('mstyle');
492                         $node_1->setAttrArr($attr_arr);
493                         
494                         $node_arr = $this->parseExpr();
495                         
496                         $node_1->addChildArr($node_arr);
497                         $node_0->addChild($node_1);
498                 } else {
499                         $node_arr = $this->parseExpr();
500                         $node_0->addChildArr($node_arr);
501                 }
502                 
503                 return TRUE;
504         }
505         
506         /*
507         function  mergeNodeArr(&$node_arr_0,&$node_arr_1)
508         {
509                 $key_arr_0 = array_keys($node_arr_0);
510                 $key_arr_1 = array_keys($node_arr_1);
511                 
512                 $num_key_0 = count($key_arr_0);
513                 $num_key_1 = count($key_arr_1);
514                 
515                 $merge_arr = array();
516                 
517                 for ($i = 0;$i < $num_key_0;$i++) {
518                         $merge_arr[$key_arr_0[$i]] = $node_arr_0[$key_arr_0[$i]];
519                 }
520                 
521                 for ($j = 0;$j < $num_key_1;$i++) {
522                         $merge_arr[$key_arr_1[$i]] = $node_arr_1[$key_arr_1[$i]];
523                 }
524                 
525                 return($merge_arr);
526         }
527         */
528         
529         //Broken out of parseExpr Sept 7, 2006 David Lippman for
530         //ASCIIMathML 1.4.7 compatibility
531         function  parseIntExpr()
532         {
533                 $sym_0 = $this->getSymbol();
534                 $node_0 = $this->parseSmplExpr();
535                 $sym = $this->getSymbol();
536                 
537                 if (isset($sym['infix']) && $sym['input'] != '/') {
538                         $this->chopExpr($sym['symlen']);
539                         $node_1 = $this->parseSmplExpr();
540                         
541                         if ($node_1 === FALSE) { //show box in place of missing argument
542                                 $node_1 = $this->emptyNode();//??
543                         } else {
544                                 $node_1->removeBrackets();
545                         }
546                         
547                         // If 'sub' -- subscript
548                         if ($sym['input'] == '_') {
549                                 
550                                 $sym_1 = $this->getSymbol();
551                                 
552                                 // If 'sup' -- superscript
553                                 if ($sym_1['input'] == '^') { 
554                                         $this->chopExpr($sym_1['symlen']);
555                                         $node_2 = $this->parseSmplExpr();
556                                         $node_2->removeBrackets();
557                                         
558                                         $node_3 = $this->createNode();
559                                         $node_3->setName(isset($sym_0['underover']) ? 'munderover' : 'msubsup');
560                                         $node_3->addChild($node_0);
561                                         $node_3->addChild($node_1);
562                                         $node_3->addChild($node_2);
563                                         
564                                         $node_4 = $this->createNode();
565                                         $node_4->setName('mrow');
566                                         $node_4->addChild($node_3);
567                                         
568                                         return $node_4;
569                                 } else {
570                                         $node_2 = $this->createNode();
571                                         $node_2->setName(isset($sym_0['underover']) ? 'munder' : 'msub');
572                                         $node_2->addChild($node_0);
573                                         $node_2->addChild($node_1);
574                                         
575                                         return $node_2;
576                                 }
577                         } else {
578                                 $node_2 = $this->createNode();
579                                 $node_2->setName($sym['tag']);
580                                 $node_2->addChild($node_0);
581                                 $node_2->addChild($node_1);
582                                 
583                                 return($node_2);
584                         }
585                 } elseif ($node_0 !== FALSE) {
586                         return($node_0);
587                 } else {
588                         return $this->emptyNode();
589                 }
590                 
591         }
592         
593         function  parseExpr()
594         {
595                 // Child/Fragment array
596                 $node_arr = array();
597                 
598                 // Deal whole expressions like 'ax + by + c = 0' etc.
599                 do {
600                         $sym_0 = $this->getSymbol();
601                         $node_0 = $this->parseIntExpr();
602                         $sym = $this->getSymbol();
603                         // var_dump($sym);
604                         
605                         if (isset($sym['infix']) && $sym['input'] == '/') {
606                                 $this->chopExpr($sym['symlen']);
607                                 $node_1 = $this->parseIntExpr();
608                                 
609                                 if ($node_1 === FALSE) { //should show box in place of missing argument
610                                         $node_1 = $this->emptyNode();
611                                         continue;
612                                 }
613
614                                 $node_1->removeBrackets();
615                                 
616                                 // If 'div' -- divide
617                                 $node_0->removeBrackets();
618                                 $node_2 = $this->createNode();
619                                 $node_2->setName($sym['tag']);
620                                 $node_2->addChild($node_0);
621                                 $node_2->addChild($node_1);
622                                 $node_arr[$node_2->getId()] = $node_2;
623                         
624                         } elseif ($node_0 !== FALSE) {
625                                 $node_arr[$node_0->getId()] = $node_0;
626                         }
627                 } while (!isset($sym['right_bracket']) && $sym !== FALSE && $sym['output'] != '');
628                 
629                 //var_dump($sym);
630                 // Possibly to deal with matrices
631                 if (isset($sym['right_bracket'])) {
632                         $node_cnt = count($node_arr);
633                         $key_node_arr = array_keys($node_arr);
634                         
635                         if ($node_cnt > 1) {
636                                 $node_5 = $node_arr[$key_node_arr[$node_cnt-1]];
637                                 $node_6 = $node_arr[$key_node_arr[$node_cnt-2]];
638                         } else {
639                                 $node_5 = FALSE;
640                                 $node_6 = FALSE;
641                         }
642                         
643                         // Dealing with matrices
644                         if ($node_5 !== FALSE && $node_6 !== FALSE &&
645                                 $node_cnt > 1 && 
646                                 $node_5->getName() == 'mrow' && 
647                                 $node_6->getName() == 'mo' &&
648                                 $node_6->getContent() == ',') {
649                                 
650                                 // Checking if Node 5 has a LastChild
651                                 if ($node_7 = $node_5->getLastChild()) {
652                                         $node_7_cntnt = $node_7->getContent();
653                                 } else {
654                                         $node_7_cntnt = FALSE;
655                                 }
656                                 
657                                 // If there is a right bracket
658                                 if ($node_7 !== FALSE && ($node_7_cntnt == ']' || $node_7_cntnt == ')')) {
659                                         
660                                         // Checking if Node 5 has a firstChild
661                                         if ($node_8 = $node_5->getFirstChild()) {
662                                                 $node_8_cntnt = $node_8->getContent();
663                                         } else {
664                                                 $node_8_cntnt = FALSE;
665                                         }
666                                         
667                                         // If there is a matching left bracket
668                                         if ($node_8 !== FALSE && 
669                                                 (($node_8_cntnt == '(' && $node_7_cntnt == ')' && $sym['output'] != '}') ||
670                                                 ($node_8_cntnt == '[' && $node_7_cntnt == ']'))) {
671                                                         
672                                                 $is_mtrx_flg = TRUE;
673                                                 $comma_pos_arr = array();
674                                                 
675                                                 $i = 0;
676                                                 
677                                                 while ($i < $node_cnt && $is_mtrx_flg) {
678                                                         $tmp_node = $node_arr[$key_node_arr[$i]];
679                                                         
680                                                         if($tmp_node_first = $tmp_node->getFirstChild()) {
681                                                                 $tnfc = $tmp_node_first->getContent();
682                                                         } else {
683                                                                 $tnfc = FALSE;
684                                                         }
685                                                         
686                                                         if($tmp_node_last = $tmp_node->getLastChild()) {
687                                                                 $tnlc = $tmp_node_last->getContent();
688                                                         } else {
689                                                                 $tnlc = FALSE;
690                                                         }
691                                                         
692                                                         if (isset($key_node_arr[$i+1])) {
693                                                                 $next_tmp_node = $node_arr[$key_node_arr[$i+1]];
694                                                                 $ntnn = $next_tmp_node->getName();
695                                                                 $ntnc = $next_tmp_node->getContent();
696                                                         } else {
697                                                                 $ntnn = FALSE;
698                                                                 $ntnc = FALSE;
699                                                         }
700                                                         
701                                                         // Checking each node in node array for matrix criteria
702                                                         if ($is_mtrx_flg) {
703                                                                 $is_mtrx_flg = $tmp_node->getName() == 'mrow' &&
704                                                                         ($i == $node_cnt-1 || $ntnn == 'mo' && $ntnc == ',') &&
705                                                                         $tnfc == $node_8_cntnt && $tnlc == $node_7_cntnt;
706                                                         }
707
708                                                         if ($is_mtrx_flg) {
709                                                                 for ($j = 0;$j < $tmp_node->getNumChild();$j++) {
710                                                                         $tmp_c_node = $tmp_node->getChildByIdx($j);
711                                                                         
712                                                                         if ($tmp_c_node->getContent() == ',') {
713                                                                                 $comma_pos_arr[$i][] = $j;
714                                                                         }
715                                                                 }
716                                                         }       
717                                                         
718                                                         if ($is_mtrx_flg && $i > 1) {
719                                                                 
720                                                                 $cnt_cpan = isset($comma_pos_arr[$i]) ? count($comma_pos_arr[$i]) : NULL;
721                                                                 $cnt_cpap = isset($comma_pos_arr[$i-2]) ? count($comma_pos_arr[$i-2]) : NULL;
722                                                                 $is_mtrx_flg = $cnt_cpan == $cnt_cpap;
723                                                         }
724                                                         
725                                                         $i += 2;
726                                                 }
727                                                 
728                                                 // If the node passes the matrix tests
729                                                 if ($is_mtrx_flg) {
730                                                         $tab_node_arr = array();
731                                                         
732                                                         for ($i = 0;$i < $node_cnt;$i += 2) {
733                                                                 $tmp_key_node_arr = array_keys($node_arr);
734                                                                 if (!($tmp_node = $node_arr[$tmp_key_node_arr[0]])) {
735                                                                         break;
736                                                                 }
737                                                                 $num_child = $tmp_node->getNumChild();
738                                                                 $k = 0;
739                                                                 
740                                                                 $tmp_node->removeFirstChild();
741                                                                 
742                                                                 $row_node_arr = array();
743                                                                 $row_frag_node_arr = array();
744                                                                 
745                                                                 for ($j = 1;$j < ($num_child-1);$j++) {
746                                                                         if (isset($comma_pos_arr[$i][$k]) && 
747                                                                                 $j == $comma_pos_arr[$i][$k]) {
748                                                                                 
749                                                                                 $tmp_node->removeFirstChild();
750                                                                                 
751                                                                                 $tmp_c_node = $this->createNode();
752                                                                                 $tmp_c_node->setName('mtd');
753                                                                                 $tmp_c_node->addChildArr($row_frag_node_arr);
754                                                                                 $row_frag_node_arr = array();
755                                                                                 
756                                                                                 $row_node_arr[$tmp_c_node->getId()] = $tmp_c_node;
757                                                                                 
758                                                                                 $k++;
759                                                                         } else {
760                                                                                 
761                                                                                 if ($tmp_c_node = $tmp_node->getFirstChild()) {
762                                                                                         $row_frag_node_arr[$tmp_c_node->getId()] = $tmp_c_node;
763                                                                                         $tmp_node->removeFirstChild();
764                                                                                 }
765                                                                         }
766                                                                 }
767                                                                 
768                                                                 $tmp_c_node = $this->createNode();
769                                                                 $tmp_c_node->setName('mtd');
770                                                                 $tmp_c_node->addChildArr($row_frag_node_arr);
771                                                                 
772                                                                 $row_node_arr[$tmp_c_node->getId()] = $tmp_c_node;
773                                                                 
774                                                                 if (count($node_arr) > 2) {
775                                                                         $tmp_key_node_arr = array_keys($node_arr);
776                                                                         unset($node_arr[$tmp_key_node_arr[0]]);
777                                                                         unset($node_arr[$tmp_key_node_arr[1]]);
778                                                                 }
779                                                                 
780                                                                 $tmp_c_node = $this->createNode();
781                                                                 $tmp_c_node->setName('mtr');
782                                                                 $tmp_c_node->addChildArr($row_node_arr);
783                                                                 
784                                                                 $tab_node_arr[$tmp_c_node->getId()] = $tmp_c_node;
785                                                         }
786                                                         
787                                                         $tmp_c_node = $this->createNode();
788                                                         $tmp_c_node->setName('mtable');
789                                                         $tmp_c_node->addChildArr($tab_node_arr);
790                                                         
791                                                         if (isset($sym['invisible'])) {
792                                                                 $tmp_c_node->setAttr('columnalign','left');
793                                                         }
794                                                         
795                                                         $key_node_arr = array_keys($node_arr);
796                                                         $tmp_c_node->setId($key_node_arr[0]);
797                                                         
798                                                         $node_arr[$tmp_c_node->getId()] = $tmp_c_node;
799                                                 }
800                                         }
801                                 }
802                         }
803                         
804                         $this->chopExpr($sym['symlen']);
805                         if (!isset($sym['invisible'])) {
806                                 $node_7 = $this->createNode();
807                                 $node_7->setName('mo');
808                                 $node_7->setContent($sym['output']);
809                                 $node_arr[$node_7->getId()] = $node_7;
810                         }
811                 }
812                 
813                 return($node_arr);
814         }
815         
816         function  parseSmplExpr()
817         {
818                 $sym = $this->getSymbol();
819                 
820                 if (!$sym || isset($sym['right_bracket'])) //return FALSE; 
821                         return $this->emptyNode();
822                 
823                 $this->chopExpr($sym['symlen']);
824                 
825                 // 2005-06-11 wes: add definition type support
826                 if(isset($sym['definition'])) {
827                         $this->pushExpr($sym['output']);
828                         $sym = $this->getSymbol();
829                         $this->chopExpr($sym['symlen']);
830                 }
831
832                 if (isset($sym['left_bracket'])) {
833                         $node_arr = $this->parseExpr();
834                         
835                         if (isset($sym['invisible'])) {
836                                 $node_0 = $this->createNode();
837                                 $node_0->setName('mrow');
838                                 $node_0->addChildArr($node_arr);
839                                 
840                                 return($node_0);
841                         } else {
842                                 $node_0 = $this->createNode();
843                                 $node_0->setName('mo');
844                                 $node_0->setContent($sym['output']);
845                                 
846                                 $node_1 = $this->createNode();
847                                 $node_1->setName('mrow');
848                                 $node_1->addChild($node_0);
849                                 $node_1->addChildArr($node_arr);
850                                 
851                                 return($node_1);
852                         }
853                 } elseif (isset($sym['unary'])) {
854                         
855                         if ($sym['input'] == 'sqrt') {
856                                 $node_0 = $this->parseSmplExpr();
857                                 $node_0->removeBrackets();
858                                 
859                                 $node_1 = $this->createNode();
860                                 $node_1->setName($sym['tag']);
861                                 $node_1->addChild($node_0);
862                                 
863                                 return($node_1);
864                         } elseif (isset($sym['func'])) { //added 2006-9-7 David Lippman
865                                 $expr = ltrim($this->getCurrExpr());
866                                 $st = $expr{0};
867                                 $node_0 = $this->parseSmplExpr();
868                                 //$node_0->removeBrackets();
869                                 if ($st=='^' || $st == '_' || $st=='/' || $st=='|' || $st==',') {
870                                         $node_1 = $this->createNode();
871                                         $node_1->setName($sym['tag']);
872                                         $node_1->setContent($sym['output']);
873                                         $this->setCurrExpr($expr);
874                                         return($node_1);
875                                 } else {
876                                         $node_1 = $this->createNode();
877                                         $node_1->setName('mrow');
878                                         $node_2 = $this->createNode();
879                                         $node_2->setName($sym['tag']);
880                                         $node_2->setContent($sym['output']);
881                                         $node_1->addChild($node_2);
882                                         $node_1->addChild($node_0);
883                                         return($node_1);
884                                 }
885                         } elseif ($sym['input'] == 'text' || $sym['input'] == 'mbox' || $sym['input'] == '"') {
886                                 $expr = ltrim($this->getCurrExpr());
887                                 if ($sym['input']=='"') {
888                                         $end_brckt = '"';
889                                         $txt = substr($expr,0,strpos($expr,$end_brckt));
890                                 } else {
891                                         switch($expr{0}) {
892                                                 case '(':
893                                                         $end_brckt = ')';
894                                                         break;
895                                                 case '[':
896                                                         $end_brckt = ']';
897                                                         break;
898                                                 case '{':
899                                                         $end_brckt = '}';
900                                                         break;
901                                                 default:
902                                                         $end_brckt = chr(11); // A character that will never be matched.
903                                                         break;
904                                         }
905                                         $txt = substr($expr,1,strpos($expr,$end_brckt)-1);
906                                 }
907                                 
908                                 //$txt = substr($expr,1,strpos($expr,$end_brckt)-1);
909                                 $len = strlen($txt);
910                                 
911                                 $node_0 = $this->createNode();
912                                 $node_0->setName('mrow');
913                                 
914                                 if ($len > 0) {
915                                         if ($txt{0} == " ") {
916                                                 $node_1 = $this->createNode();
917                                                 $node_1->setName('mspace');
918                                                 $node_1->setAttr('width','1ex');
919                                                 
920                                                 $node_0->addChild($node_1);
921                                         }
922                                         
923                                         $node_3 = $this->createNode();
924                                         $node_3->setName($sym['tag']);
925                                         $node_3->setContent(trim($txt));
926                                         
927                                         $node_0->addChild($node_3);
928                                         
929                                         if ($len > 1 && $txt{$len-1} == " ") {
930                                                 $node_2 = $this->createNode();
931                                                 $node_2->setName('mspace');
932                                                 $node_2->setAttr('width','1ex');
933                                                 
934                                                 $node_0->addChild($node_2);
935                                         }
936                                         
937                                         $this->chopExpr($len+2);
938                                 }
939                                 return($node_0);
940                                 
941                         } elseif (isset($sym['acc'])) {
942                                 $node_0 = $this->parseSmplExpr();
943                                 $node_0->removeBrackets();
944                                 
945                                 $node_1 = $this->createNode();
946                                 $node_1->setName($sym['tag']);
947                                 $node_1->addChild($node_0);
948                                 
949                                 $node_2 = $this->createNode();
950                                 $node_2->setName('mo');
951                                 $node_2->setContent($sym['output']);
952                                 
953                                 $node_1->addChild($node_2);
954                                 return($node_1);
955                         } else {
956                                 // Font change commands -- to complete
957                         }
958                 } elseif (isset($sym['binary'])) {
959                         $node_arr = array();
960                         
961                         $node_0 = $this->parseSmplExpr();
962                         $node_0->removeBrackets();
963                         
964                         $node_1 = $this->parseSmplExpr();
965                         $node_1->removeBrackets();
966                         
967             /* 2005-06-05 wes: added stackrel */
968                         if ($sym['input'] == 'root' || $sym['input'] == 'stackrel') {
969                                 $node_arr[$node_1->getId()] = $node_1;
970                                 $node_arr[$node_0->getId()] = $node_0;
971                         } elseif ($sym['input'] == 'frac') {
972                                 $node_arr[$node_0->getId()] = $node_0;
973                                 $node_arr[$node_1->getId()] = $node_1;
974                         }
975                         
976                         $node_2 = $this->createNode();
977                         $node_2->setName($sym['tag']);
978                         $node_2->addChildArr($node_arr);
979                         
980                         return($node_2);
981                 } elseif (isset($sym['infix'])) {
982                         $node_0 = $this->createNode();
983                         $node_0->setName('mo');
984                         $node_0->setContent($sym['output']);
985                         
986                         return($node_0);
987                 } elseif (isset($sym['space'])) {
988                         $node_0 = $this->createNode();
989                         $node_0->setName('mrow');
990                         
991                         $node_1 = $this->createNode();
992                         $node_1->setName('mspace');
993                         $node_1->setAttr('width',$sym['space']);
994                         
995                         $node_2 = $this->createNode();
996                         $node_2->setName($sym['tag']);
997                         $node_2->setContent($sym['output']);
998                         
999                         $node_3 = $this->createNode();
1000                         $node_3->setName('mspace');
1001                         $node_3->setAttr('width',$sym['space']);
1002                         
1003                         $node_0->addChild($node_1);
1004                         $node_0->addChild($node_2);
1005                         $node_0->addChild($node_3);
1006                         
1007                         return($node_0);
1008                 } else {
1009                         
1010                         // A constant
1011                         $node_0 = $this->createNode();
1012                         $node_0->setName($sym['tag']);
1013                         $node_0->setContent($sym['output']);
1014                         return($node_0);
1015                 }
1016                 
1017                 // Return an empty node
1018                 return $this->emptyNode();
1019         }
1020         
1021         function getMathML()
1022         {
1023                 $root = $this->_node_arr[0];
1024                 return($root->dumpXML());
1025         }
1026         
1027         function getCurrExpr()
1028         {
1029                 return($this->_curr_expr);
1030         }
1031         
1032         function setCurrExpr($str)
1033         {
1034                 $this->_curr_expr = $str;
1035         }
1036         
1037         function getExpr()
1038         {
1039                 return($this->_expr);
1040         }
1041         
1042         function getPrevExpr()
1043         {
1044                 return($this->_prev_expr);
1045         }
1046         
1047         function  createNode()
1048         {
1049                 $node = new MathMLNode($this->_node_cntr);
1050                 // $node->setNamespaceAlias('m');
1051                 $this->_node_arr[$this->_node_cntr] = $node;
1052                 $this->_node_cntr++;
1053                 return($node);
1054         }
1055         
1056         /**
1057          * Gets the largest symbol in the expression (greedy). Changed from non-greedy 26-Apr-2006
1058          * 
1059          * @parameter boolean[optional] Chop original string?
1060          *
1061          * @return mixed
1062          * 
1063          * @access private
1064          */
1065         function getSymbol($chop_flg = FALSE)
1066         {
1067                 // Implemented a reverse symbol matcher. 
1068                 // Instead of going front to back, it goes back to front. Steven 26-Apr-2006
1069                 $chr_cnt = strlen($this->_curr_expr);
1070                 
1071                 if ($chr_cnt == 0) return FALSE;
1072                 
1073                 for ($i = $chr_cnt; $i > 0; $i--) {
1074                         $sym_0 = substr($this->_curr_expr,0,$i);
1075                         
1076                         // Reading string for numeric values
1077                         if (is_numeric($sym_0)) {
1078                                 
1079                                 if ($chop_flg) $this->chopExpr($i);
1080                                 return array('input'=>$sym_0, 'tag'=>'mn', 'output'=>$sym_0, 'symlen'=>$i);
1081                                 
1082                         } elseif (isset($this->_symbol_arr[$sym_0])) {
1083                                 
1084                                 if ($chop_flg) $this->chopExpr($i);
1085                                 $sym_arr = $this->_symbol_arr[$sym_0];
1086                                 $sym_arr['symlen'] = $i;
1087                                 return $sym_arr;
1088                         }
1089                 }
1090                 
1091                 // Reading string for alphabetic constants and the minus sign
1092                 $char = $this->_curr_expr{0};
1093                 $len_left = $chop_flg ? $this->chopExpr(1) : strlen($this->_curr_expr)-1;
1094                 
1095                 // Deals with expressions of length 1
1096                 if ($len_left == 0 && isset($this->_symbol_arr[$char])) {
1097                         $sym_arr = $this->_symbol_arr[$char];
1098                         $sym_arr['symlen'] = 1;
1099                         return $sym_arr;
1100                 } else {
1101                         $tag = preg_match('/[a-z]/i',$char) ? 'mi' : 'mo';
1102                         return array('input'=>$char, 'tag'=>$tag, 'output'=>$char, 'symlen'=>1);
1103                 }
1104         }
1105         
1106         function chopExpr($strlen)
1107         {
1108                 $this->_prev_expr = $this->_curr_expr;
1109                 
1110                 if ($strlen == strlen($this->_curr_expr)) {
1111                         $this->_curr_expr = '';
1112                         return(0);
1113                 } else {
1114                         $this->_curr_expr = ltrim(substr($this->_curr_expr,$strlen));
1115                         return(strlen($this->_curr_expr));
1116                 }
1117         }       
1118 }
1119 ?>