made to bring ASCIIMathPHP up to * ASCIIMath 1.4.7 functionality. * -- Added parseIntExpr, for intermediate expression parsing rule, allowing x^2/x^3 to render as (x^2)/(x^3) * -- Added quotes as another way of designating text; "hello" is equivalent to text(hello) * -- Added FUNC designator to allow sin, cos, etc to act as functions, so sin(x)/x renders as {sin(x)}/x * * Ver 1.11 * -- Fixed bug that stopped script execution for incomplete expressions * -- Changed the algorithm for parsing expressions so that it matches the longest string possible (greedy) * * Ver 1.10 * -- Added definition support * -- Added stackrel support * -- Added a bunch of different symbols etc. >>, << and definitions like dx, dy, dz etc. * * Ver 1.02 * -- Fixed bug with mbox and text * -- Fixed spacing bug with mbox and text * * Ver 1.01 * -- Fixed Bug that did not parse symbols greater than a single character * correctly when appearing at end of expression. * ***/ class XMLNode { // Private variables var $_id; var $_name; var $_content; var $_mt_elem_flg; var $_attr_arr; var $_child_arr; var $_nmspc; var $_nmspc_alias; var $_parent_id; var $_parent_node; function XMLNode($id = NULL) { $this->_id = isset($id) ? $id : md5(uniqid(rand(),1)); $this->_name = ''; $this->_content = ''; $this->_mt_elem_flg = FALSE; $this->_attr_arr = array(); $this->_child_arr = array(); $this->_nmspc = ''; $this->_nmspc_alias = ''; $this->_parent_id = FALSE; $this->_parent_node = NULL; } function addChild(&$node) { $this->_child_arr[$node->getId()] = $node; $node->setParentId($this->_id); $node->setParentNode($this); } function addChildArr(&$node_arr) { $key_arr = array_keys($node_arr); $num_key = count($key_arr); for ($i = 0; $i < $num_key; $i++) { $node = $node_arr[$key_arr[$i]]; $this->addChild($node); } } function insertChildBefore($idx,&$node) { $key_arr = array_keys($this->_child_arr); $num_key = count($key_arr); $tmp_arr = arry(); for ($i = 0;$i < $num_key;$i++) { if ($i == $idx) { $tmp_arr[$node->getId()] = $node; } $tmp_arr[$key_arr[$i]] = $this->_child_arr[$key_arr[$i]]; } $this->_child_arr = $tmp_arr; } function insertChildAfter($idx,&$node) { $key_arr = array_keys($this->_child_arr); $num_key = count($key_arr); $tmp_arr = arry(); for ($i = 0;$i < $num_key;$i++) { $tmp_arr[$key_arr[$i]] = $this->_child_arr[$key_arr[$i]]; if ($i == $idx) { $tmp_arr[$node->getId()] = $node; } } $this->_child_arr = $tmp_arr; } function setId($id) { $this->_id = $id; } function setName($name) { $this->_name = $name; } function setNamepace($nmspc) { $this->_nmspc = $nmspc; } function setNamespaceAlias($nmspc_alias) { $this->_nmspc_alias = $nmspc_alias; } function setContent($content) { $this->_content = $content; } function setEmptyElem($mt_elem_flg) { $this->_mt_elem_flg = $mt_elem_flg; } function setAttr($attr_nm,$attr_val) { $this->_attr_arr[$attr_nm] = $attr_val; } function setAttrArr($attr_arr) { $this->_attr_arr = $attr_arr; } function setParentId($id) { $this->_parent_id = $id; } function setParentNode(&$node) { $this->_parent_node = $node; } function getId() { return($this->_id); } function getName() { return($this->_name); } function getNamespace() { return($this->_nmspc); } function getNamespaceAlias() { return($this->_nmspc_alias); } function getContent() { return($this->_content); } function getAttr($attr_nm) { if (isset($this->_attr_arr[$attr_nm])) { return($this->_attr_arr[$attr_nm]); } else { return(NULL); } } function getAttrArr() { return($this->_attr_arr); } function getParentId() { return($this->parent_id); } function getParentNode() { return($this->_parent_node); } function getChild($id) { if (isset($this->_child_arr[$id])) { return($this->_child_arr[$id]); } else { return(FALSE); } } function getFirstChild() { $id_arr = array_keys($this->_child_arr); $num_child = count($id_arr); if ($num_child > 0) { return($this->_child_arr[$id_arr[0]]); } else { return(FALSE); } } function getLastChild() { $id_arr = array_keys($this->_child_arr); $num_child = count($id_arr); if ($num_child > 0) { return($this->_child_arr[$id_arr[$num_child - 1]]); } else { return(FALSE); } } function getChildByIdx($idx) { $id_arr = array_keys($this->_child_arr); if (isset($this->_child_arr[$id_arr[$idx]])) { return($this->_child_arr[$id_arr[$idx]]); } else { return(FALSE); } } function getNumChild() { return(count($this->_child_arr)); } function removeChild($id) { unset($this->_child_arr[$id]); } function removeChildByIdx($idx) { $key_arr = array_keys($this->_child_arr); unset($this->_child_arr[$key_arr[$idx]]); } function removeFirstChild() { $key_arr = array_keys($this->_child_arr); unset($this->_child_arr[$key_arr[0]]); } function removeLastChild() { $key_arr = array_keys($this->_child_arr); unset($this->_child_arr[$key_arr[count($key_arr)-1]]); } function dumpXML($indent_str = "\t") { $attr_txt = $this->_dumpAttr(); $name = $this->_dumpName(); $xmlns = $this->_dumpXmlns(); $lvl = $this->_getCurrentLevel(); $indent = str_pad('',$lvl,$indent_str); if ($this->_mt_elem_flg) { $tag = "$indent<$name$xmlns$attr_txt />"; return($tag); } else { $key_arr = array_keys($this->_child_arr); $num_child = count($key_arr); $tag = "$indent<$name$xmlns$attr_txt>$this->_content"; for ($i = 0;$i < $num_child;$i++) { $node = $this->_child_arr[$key_arr[$i]]; $child_txt = $node->dumpXML($indent_str); $tag .= "\n$child_txt"; } $tag .= ($num_child > 0 ? "\n$indent" : ""); return($tag); } } function _dumpAttr() { $id_arr = array_keys($this->_attr_arr); $id_arr_cnt = count($id_arr); $attr_txt = ''; for($i = 0;$i < $id_arr_cnt;$i++) { $key = $id_arr[$i]; $attr_txt .= " $key=\"{$this->_attr_arr[$key]}\""; } return($attr_txt); } function _dumpName() { $alias = $this->getNamespaceAlias(); if ($alias == '') { return($this->getName()); } else { return("$alias:" . $this->getName()); } } function _dumpXmlns() { $nmspc = $this->getNamespace(); $alias = $this->getNamespaceAlias(); if ($nmspc != '') { if ($alias == '') { return(" xmlns=\"" . $nmspc . "\""); } else { return(" xmlns:$alias=\"" . $nmspc . "\""); } } else { return(''); } } function _getCurrentLevel() { if ($this->_parent_id === FALSE) { return(0); } else { $node = $this->getParentNode(); $lvl = $node->_getCurrentLevel(); $lvl++; return($lvl); } } } class MathMLNode extends XMLNode { function MathMLNode($id = NULL) { parent::XMLNode($id); } function removeBrackets() { if ($this->_name == 'mrow') { if ($c_node_0 = $this->getFirstChild()) { $c_node_0->isLeftBracket() ? $this->removeFirstChild() : 0; } if ($c_node_0 = $this->getLastChild()) { $c_node_0->isRightBracket() ? $this->removeLastChild() : 0; } } } function isLeftBracket() { switch ($this->_content) { case '{': case '[': case '(': return(TRUE); break; } return(FALSE); } function isRightBracket() { switch ($this->_content) { case '}': case ']': case ')': return(TRUE); break; } return(FALSE); } } class ASCIIMathPHP { var $_expr; var $_curr_expr; var $_prev_expr; var $_symbol_arr; var $_node_arr; var $_node_cntr; function ASCIIMathPHP($symbol_arr,$expr = NULL) { $this->_symbol_arr = $symbol_arr; if (isset($expr)) { $this->setExpr($expr); } } /** * Returns an empty node (containing a non-breaking space) 26-Apr-2006 * * Used when an expression is incomplete * * @return object * * @access private */ function emptyNode() { $tmp_node = $this->createNode(); $tmp_node->setName('mn'); $tmp_node->setContent('&#' . hexdec('200B') . ';'); return $tmp_node; } function pushExpr($prefix) // 2005-06-11 wes { $this->_curr_expr = $prefix . $this->_curr_expr; } function setExpr($expr) { $this->_expr = $expr; $this->_curr_expr = $expr; $this->_prev_expr = $expr; $this->_node_arr = array(); $this->_node_cntr = 0; } function genMathML($attr_arr = NULL) { // node $node_0 = $this->createNode(); $node_0->setName('math'); $node_0->setNamepace('http://www.w3.org/1998/Math/MathML'); // node if (isset($attr_arr)) { $node_1 = $this->createNode(); $node_1->setName('mstyle'); $node_1->setAttrArr($attr_arr); $node_arr = $this->parseExpr(); $node_1->addChildArr($node_arr); $node_0->addChild($node_1); } else { $node_arr = $this->parseExpr(); $node_0->addChildArr($node_arr); } return TRUE; } /* function mergeNodeArr(&$node_arr_0,&$node_arr_1) { $key_arr_0 = array_keys($node_arr_0); $key_arr_1 = array_keys($node_arr_1); $num_key_0 = count($key_arr_0); $num_key_1 = count($key_arr_1); $merge_arr = array(); for ($i = 0;$i < $num_key_0;$i++) { $merge_arr[$key_arr_0[$i]] = $node_arr_0[$key_arr_0[$i]]; } for ($j = 0;$j < $num_key_1;$i++) { $merge_arr[$key_arr_1[$i]] = $node_arr_1[$key_arr_1[$i]]; } return($merge_arr); } */ //Broken out of parseExpr Sept 7, 2006 David Lippman for //ASCIIMathML 1.4.7 compatibility function parseIntExpr() { $sym_0 = $this->getSymbol(); $node_0 = $this->parseSmplExpr(); $sym = $this->getSymbol(); if (isset($sym['infix']) && $sym['input'] != '/') { $this->chopExpr($sym['symlen']); $node_1 = $this->parseSmplExpr(); if ($node_1 === FALSE) { //show box in place of missing argument $node_1 = $this->emptyNode();//?? } else { $node_1->removeBrackets(); } // If 'sub' -- subscript if ($sym['input'] == '_') { $sym_1 = $this->getSymbol(); // If 'sup' -- superscript if ($sym_1['input'] == '^') { $this->chopExpr($sym_1['symlen']); $node_2 = $this->parseSmplExpr(); $node_2->removeBrackets(); $node_3 = $this->createNode(); $node_3->setName(isset($sym_0['underover']) ? 'munderover' : 'msubsup'); $node_3->addChild($node_0); $node_3->addChild($node_1); $node_3->addChild($node_2); $node_4 = $this->createNode(); $node_4->setName('mrow'); $node_4->addChild($node_3); return $node_4; } else { $node_2 = $this->createNode(); $node_2->setName(isset($sym_0['underover']) ? 'munder' : 'msub'); $node_2->addChild($node_0); $node_2->addChild($node_1); return $node_2; } } else { $node_2 = $this->createNode(); $node_2->setName($sym['tag']); $node_2->addChild($node_0); $node_2->addChild($node_1); return($node_2); } } elseif ($node_0 !== FALSE) { return($node_0); } else { return $this->emptyNode(); } } function parseExpr() { // Child/Fragment array $node_arr = array(); // Deal whole expressions like 'ax + by + c = 0' etc. do { $sym_0 = $this->getSymbol(); $node_0 = $this->parseIntExpr(); $sym = $this->getSymbol(); // var_dump($sym); if (isset($sym['infix']) && $sym['input'] == '/') { $this->chopExpr($sym['symlen']); $node_1 = $this->parseIntExpr(); if ($node_1 === FALSE) { //should show box in place of missing argument $node_1 = $this->emptyNode(); continue; } $node_1->removeBrackets(); // If 'div' -- divide $node_0->removeBrackets(); $node_2 = $this->createNode(); $node_2->setName($sym['tag']); $node_2->addChild($node_0); $node_2->addChild($node_1); $node_arr[$node_2->getId()] = $node_2; } elseif ($node_0 !== FALSE) { $node_arr[$node_0->getId()] = $node_0; } } while (!isset($sym['right_bracket']) && $sym !== FALSE && $sym['output'] != ''); //var_dump($sym); // Possibly to deal with matrices if (isset($sym['right_bracket'])) { $node_cnt = count($node_arr); $key_node_arr = array_keys($node_arr); if ($node_cnt > 1) { $node_5 = $node_arr[$key_node_arr[$node_cnt-1]]; $node_6 = $node_arr[$key_node_arr[$node_cnt-2]]; } else { $node_5 = FALSE; $node_6 = FALSE; } // Dealing with matrices if ($node_5 !== FALSE && $node_6 !== FALSE && $node_cnt > 1 && $node_5->getName() == 'mrow' && $node_6->getName() == 'mo' && $node_6->getContent() == ',') { // Checking if Node 5 has a LastChild if ($node_7 = $node_5->getLastChild()) { $node_7_cntnt = $node_7->getContent(); } else { $node_7_cntnt = FALSE; } // If there is a right bracket if ($node_7 !== FALSE && ($node_7_cntnt == ']' || $node_7_cntnt == ')')) { // Checking if Node 5 has a firstChild if ($node_8 = $node_5->getFirstChild()) { $node_8_cntnt = $node_8->getContent(); } else { $node_8_cntnt = FALSE; } // If there is a matching left bracket if ($node_8 !== FALSE && (($node_8_cntnt == '(' && $node_7_cntnt == ')' && $sym['output'] != '}') || ($node_8_cntnt == '[' && $node_7_cntnt == ']'))) { $is_mtrx_flg = TRUE; $comma_pos_arr = array(); $i = 0; while ($i < $node_cnt && $is_mtrx_flg) { $tmp_node = $node_arr[$key_node_arr[$i]]; if($tmp_node_first = $tmp_node->getFirstChild()) { $tnfc = $tmp_node_first->getContent(); } else { $tnfc = FALSE; } if($tmp_node_last = $tmp_node->getLastChild()) { $tnlc = $tmp_node_last->getContent(); } else { $tnlc = FALSE; } if (isset($key_node_arr[$i+1])) { $next_tmp_node = $node_arr[$key_node_arr[$i+1]]; $ntnn = $next_tmp_node->getName(); $ntnc = $next_tmp_node->getContent(); } else { $ntnn = FALSE; $ntnc = FALSE; } // Checking each node in node array for matrix criteria if ($is_mtrx_flg) { $is_mtrx_flg = $tmp_node->getName() == 'mrow' && ($i == $node_cnt-1 || $ntnn == 'mo' && $ntnc == ',') && $tnfc == $node_8_cntnt && $tnlc == $node_7_cntnt; } if ($is_mtrx_flg) { for ($j = 0;$j < $tmp_node->getNumChild();$j++) { $tmp_c_node = $tmp_node->getChildByIdx($j); if ($tmp_c_node->getContent() == ',') { $comma_pos_arr[$i][] = $j; } } } if ($is_mtrx_flg && $i > 1) { $cnt_cpan = isset($comma_pos_arr[$i]) ? count($comma_pos_arr[$i]) : NULL; $cnt_cpap = isset($comma_pos_arr[$i-2]) ? count($comma_pos_arr[$i-2]) : NULL; $is_mtrx_flg = $cnt_cpan == $cnt_cpap; } $i += 2; } // If the node passes the matrix tests if ($is_mtrx_flg) { $tab_node_arr = array(); for ($i = 0;$i < $node_cnt;$i += 2) { $tmp_key_node_arr = array_keys($node_arr); if (!($tmp_node = $node_arr[$tmp_key_node_arr[0]])) { break; } $num_child = $tmp_node->getNumChild(); $k = 0; $tmp_node->removeFirstChild(); $row_node_arr = array(); $row_frag_node_arr = array(); for ($j = 1;$j < ($num_child-1);$j++) { if (isset($comma_pos_arr[$i][$k]) && $j == $comma_pos_arr[$i][$k]) { $tmp_node->removeFirstChild(); $tmp_c_node = $this->createNode(); $tmp_c_node->setName('mtd'); $tmp_c_node->addChildArr($row_frag_node_arr); $row_frag_node_arr = array(); $row_node_arr[$tmp_c_node->getId()] = $tmp_c_node; $k++; } else { if ($tmp_c_node = $tmp_node->getFirstChild()) { $row_frag_node_arr[$tmp_c_node->getId()] = $tmp_c_node; $tmp_node->removeFirstChild(); } } } $tmp_c_node = $this->createNode(); $tmp_c_node->setName('mtd'); $tmp_c_node->addChildArr($row_frag_node_arr); $row_node_arr[$tmp_c_node->getId()] = $tmp_c_node; if (count($node_arr) > 2) { $tmp_key_node_arr = array_keys($node_arr); unset($node_arr[$tmp_key_node_arr[0]]); unset($node_arr[$tmp_key_node_arr[1]]); } $tmp_c_node = $this->createNode(); $tmp_c_node->setName('mtr'); $tmp_c_node->addChildArr($row_node_arr); $tab_node_arr[$tmp_c_node->getId()] = $tmp_c_node; } $tmp_c_node = $this->createNode(); $tmp_c_node->setName('mtable'); $tmp_c_node->addChildArr($tab_node_arr); if (isset($sym['invisible'])) { $tmp_c_node->setAttr('columnalign','left'); } $key_node_arr = array_keys($node_arr); $tmp_c_node->setId($key_node_arr[0]); $node_arr[$tmp_c_node->getId()] = $tmp_c_node; } } } } $this->chopExpr($sym['symlen']); if (!isset($sym['invisible'])) { $node_7 = $this->createNode(); $node_7->setName('mo'); $node_7->setContent($sym['output']); $node_arr[$node_7->getId()] = $node_7; } } return($node_arr); } function parseSmplExpr() { $sym = $this->getSymbol(); if (!$sym || isset($sym['right_bracket'])) //return FALSE; return $this->emptyNode(); $this->chopExpr($sym['symlen']); // 2005-06-11 wes: add definition type support if(isset($sym['definition'])) { $this->pushExpr($sym['output']); $sym = $this->getSymbol(); $this->chopExpr($sym['symlen']); } if (isset($sym['left_bracket'])) { $node_arr = $this->parseExpr(); if (isset($sym['invisible'])) { $node_0 = $this->createNode(); $node_0->setName('mrow'); $node_0->addChildArr($node_arr); return($node_0); } else { $node_0 = $this->createNode(); $node_0->setName('mo'); $node_0->setContent($sym['output']); $node_1 = $this->createNode(); $node_1->setName('mrow'); $node_1->addChild($node_0); $node_1->addChildArr($node_arr); return($node_1); } } elseif (isset($sym['unary'])) { if ($sym['input'] == 'sqrt') { $node_0 = $this->parseSmplExpr(); $node_0->removeBrackets(); $node_1 = $this->createNode(); $node_1->setName($sym['tag']); $node_1->addChild($node_0); return($node_1); } elseif (isset($sym['func'])) { //added 2006-9-7 David Lippman $expr = ltrim($this->getCurrExpr()); $st = $expr{0}; $node_0 = $this->parseSmplExpr(); //$node_0->removeBrackets(); if ($st=='^' || $st == '_' || $st=='/' || $st=='|' || $st==',') { $node_1 = $this->createNode(); $node_1->setName($sym['tag']); $node_1->setContent($sym['output']); $this->setCurrExpr($expr); return($node_1); } else { $node_1 = $this->createNode(); $node_1->setName('mrow'); $node_2 = $this->createNode(); $node_2->setName($sym['tag']); $node_2->setContent($sym['output']); $node_1->addChild($node_2); $node_1->addChild($node_0); return($node_1); } } elseif ($sym['input'] == 'text' || $sym['input'] == 'mbox' || $sym['input'] == '"') { $expr = ltrim($this->getCurrExpr()); if ($sym['input']=='"') { $end_brckt = '"'; $txt = substr($expr,0,strpos($expr,$end_brckt)); } else { switch($expr{0}) { case '(': $end_brckt = ')'; break; case '[': $end_brckt = ']'; break; case '{': $end_brckt = '}'; break; default: $end_brckt = chr(11); // A character that will never be matched. break; } $txt = substr($expr,1,strpos($expr,$end_brckt)-1); } //$txt = substr($expr,1,strpos($expr,$end_brckt)-1); $len = strlen($txt); $node_0 = $this->createNode(); $node_0->setName('mrow'); if ($len > 0) { if ($txt{0} == " ") { $node_1 = $this->createNode(); $node_1->setName('mspace'); $node_1->setAttr('width','1ex'); $node_0->addChild($node_1); } $node_3 = $this->createNode(); $node_3->setName($sym['tag']); $node_3->setContent(trim($txt)); $node_0->addChild($node_3); if ($len > 1 && $txt{$len-1} == " ") { $node_2 = $this->createNode(); $node_2->setName('mspace'); $node_2->setAttr('width','1ex'); $node_0->addChild($node_2); } $this->chopExpr($len+2); } return($node_0); } elseif (isset($sym['acc'])) { $node_0 = $this->parseSmplExpr(); $node_0->removeBrackets(); $node_1 = $this->createNode(); $node_1->setName($sym['tag']); $node_1->addChild($node_0); $node_2 = $this->createNode(); $node_2->setName('mo'); $node_2->setContent($sym['output']); $node_1->addChild($node_2); return($node_1); } else { // Font change commands -- to complete } } elseif (isset($sym['binary'])) { $node_arr = array(); $node_0 = $this->parseSmplExpr(); $node_0->removeBrackets(); $node_1 = $this->parseSmplExpr(); $node_1->removeBrackets(); /* 2005-06-05 wes: added stackrel */ if ($sym['input'] == 'root' || $sym['input'] == 'stackrel') { $node_arr[$node_1->getId()] = $node_1; $node_arr[$node_0->getId()] = $node_0; } elseif ($sym['input'] == 'frac') { $node_arr[$node_0->getId()] = $node_0; $node_arr[$node_1->getId()] = $node_1; } $node_2 = $this->createNode(); $node_2->setName($sym['tag']); $node_2->addChildArr($node_arr); return($node_2); } elseif (isset($sym['infix'])) { $node_0 = $this->createNode(); $node_0->setName('mo'); $node_0->setContent($sym['output']); return($node_0); } elseif (isset($sym['space'])) { $node_0 = $this->createNode(); $node_0->setName('mrow'); $node_1 = $this->createNode(); $node_1->setName('mspace'); $node_1->setAttr('width',$sym['space']); $node_2 = $this->createNode(); $node_2->setName($sym['tag']); $node_2->setContent($sym['output']); $node_3 = $this->createNode(); $node_3->setName('mspace'); $node_3->setAttr('width',$sym['space']); $node_0->addChild($node_1); $node_0->addChild($node_2); $node_0->addChild($node_3); return($node_0); } else { // A constant $node_0 = $this->createNode(); $node_0->setName($sym['tag']); $node_0->setContent($sym['output']); return($node_0); } // Return an empty node return $this->emptyNode(); } function getMathML() { $root = $this->_node_arr[0]; return($root->dumpXML()); } function getCurrExpr() { return($this->_curr_expr); } function setCurrExpr($str) { $this->_curr_expr = $str; } function getExpr() { return($this->_expr); } function getPrevExpr() { return($this->_prev_expr); } function createNode() { $node = new MathMLNode($this->_node_cntr); // $node->setNamespaceAlias('m'); $this->_node_arr[$this->_node_cntr] = $node; $this->_node_cntr++; return($node); } /** * Gets the largest symbol in the expression (greedy). Changed from non-greedy 26-Apr-2006 * * @parameter boolean[optional] Chop original string? * * @return mixed * * @access private */ function getSymbol($chop_flg = FALSE) { // Implemented a reverse symbol matcher. // Instead of going front to back, it goes back to front. Steven 26-Apr-2006 $chr_cnt = strlen($this->_curr_expr); if ($chr_cnt == 0) return FALSE; for ($i = $chr_cnt; $i > 0; $i--) { $sym_0 = substr($this->_curr_expr,0,$i); // Reading string for numeric values if (is_numeric($sym_0)) { if ($chop_flg) $this->chopExpr($i); return array('input'=>$sym_0, 'tag'=>'mn', 'output'=>$sym_0, 'symlen'=>$i); } elseif (isset($this->_symbol_arr[$sym_0])) { if ($chop_flg) $this->chopExpr($i); $sym_arr = $this->_symbol_arr[$sym_0]; $sym_arr['symlen'] = $i; return $sym_arr; } } // Reading string for alphabetic constants and the minus sign $char = $this->_curr_expr{0}; $len_left = $chop_flg ? $this->chopExpr(1) : strlen($this->_curr_expr)-1; // Deals with expressions of length 1 if ($len_left == 0 && isset($this->_symbol_arr[$char])) { $sym_arr = $this->_symbol_arr[$char]; $sym_arr['symlen'] = 1; return $sym_arr; } else { $tag = preg_match('/[a-z]/i',$char) ? 'mi' : 'mo'; return array('input'=>$char, 'tag'=>$tag, 'output'=>$char, 'symlen'=>1); } } function chopExpr($strlen) { $this->_prev_expr = $this->_curr_expr; if ($strlen == strlen($this->_curr_expr)) { $this->_curr_expr = ''; return(0); } else { $this->_curr_expr = ltrim(substr($this->_curr_expr,$strlen)); return(strlen($this->_curr_expr)); } } }