1 <?php 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 (at) 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 (at) 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 ?>