| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Text::ASCIIMathML; | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | =pod | 
| 4 |  |  |  |  |  |  | =head1 NAME | 
| 5 |  |  |  |  |  |  |  | 
| 6 |  |  |  |  |  |  | Text::ASCIIMathML - Perl extension for parsing ASCIIMathML text into MathML | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | use Text::ASCIIMathML; | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | $parser=new Text::ASCIIMathML(); | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | $parser->SetAttributes(ForMoz => 1); | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | $ASCIIMathML = "int_0^1 e^x dx"; | 
| 17 |  |  |  |  |  |  | $mathML = $parser->TextToMathML($ASCIIMathML); | 
| 18 |  |  |  |  |  |  | $mathML = $parser->TextToMathML($ASCIIMathML, [title=>$ASCIIMathML]); | 
| 19 |  |  |  |  |  |  | $mathML = $parser->TextToMathML($ASCIIMathML, undef, [displaystyle=>1]); | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | $mathMLTree = $parser->TextToMathMLTree($ASCIIMathML); | 
| 22 |  |  |  |  |  |  | $mathMLTree = $parser->TextToMathMLTree($ASCIIMathML, [title=>$ASCIIMathML]); | 
| 23 |  |  |  |  |  |  | $mathMLTree = $parser->TextToMathMLTree($ASCIIMathML,undef,[displaystyle=>1]); | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | $mathML = $mathMLTree->text(); | 
| 26 |  |  |  |  |  |  | $latex  = $mathMLTree->latex(); | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | Text::ASCIIMathML is a parser for ASCIIMathML text which produces | 
| 31 |  |  |  |  |  |  | MathML XML markup strings that are suitable for rendering by any | 
| 32 |  |  |  |  |  |  | MathML-compliant browser. | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | The parser uses the following attributes which are settable through | 
| 35 |  |  |  |  |  |  | the SetAttributes method: | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | =over 4 | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | =item C | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | Specifies that the fonts should be optimized for Netscape/Mozilla/Firefox. | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | =back | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | The output of the TextToMathML method always follows the schema | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | The first argument of TextToMathML is the ASCIIMathML text to be | 
| 48 |  |  |  |  |  |  | parsed into MathML.  The second argument is a reference to an array of | 
| 49 |  |  |  |  |  |  | attribute/value pairs to be attached to the | 
| 50 |  |  |  |  |  |  | argument is a reference to an array of attribute/value pairs for the | 
| 51 |  |  |  |  |  |  | node.  Common attributes for the | 
| 52 |  |  |  |  |  |  | "xmlns"=>"&mathml;".  Common attributes for the  node are | 
| 53 |  |  |  |  |  |  | "mathcolor" (for text color), "displaystyle"=>"true" for using display | 
| 54 |  |  |  |  |  |  | style instead of inline style, and "fontfamily". | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | =head2 ASCIIMathML markup | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | The syntax is very permissive and does not generate syntax | 
| 59 |  |  |  |  |  |  | errors. This allows mathematically incorrect expressions to be | 
| 60 |  |  |  |  |  |  | displayed, which is important for teaching purposes. It also causes | 
| 61 |  |  |  |  |  |  | less frustration when previewing formulas. | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | If you encode 'x^2' or 'a_(mn)' or 'a_{mn}' or '(x+1)/y' or 'sqrtx', | 
| 64 |  |  |  |  |  |  | you pretty much get what you expect.  The choice of grouping | 
| 65 |  |  |  |  |  |  | parenthesis is up to you (they don't have to match either). If the | 
| 66 |  |  |  |  |  |  | displayed expression can be parsed uniquely without them, they are | 
| 67 |  |  |  |  |  |  | omitted.  Most LaTeX commands are also supported, so the last two | 
| 68 |  |  |  |  |  |  | formulas above can also be written as '\frac{x+1}{y}' and '\sqrt{x}'. | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | The parser uses no operator precedence and only respects the grouping | 
| 71 |  |  |  |  |  |  | brackets, subscripts, superscript, fractions and (square) roots. This | 
| 72 |  |  |  |  |  |  | is done for reasons of efficiency and generality. The resulting MathML | 
| 73 |  |  |  |  |  |  | code can quite easily be processed further to ensure additional | 
| 74 |  |  |  |  |  |  | syntactic requirements of any particular application. | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  | =head3 The grammar | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | Here is a definition of the grammar used to parse | 
| 79 |  |  |  |  |  |  | ASCIIMathML expressions. In the Backus-Naur form given below, the | 
| 80 |  |  |  |  |  |  | letter on the left of the C<::=> represents a category of symbols that | 
| 81 |  |  |  |  |  |  | could be one of the possible sequences of symbols listed on the right. | 
| 82 |  |  |  |  |  |  | The vertical bar C<|> separates the alternatives. | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | =over 4 | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | c ::= [A-z] | numbers | greek letters | other constant symbols | 
| 87 |  |  |  |  |  |  | (see below) | 
| 88 |  |  |  |  |  |  | u ::= 'sqrt' | 'text' | 'bb' | other unary symbols for font commands | 
| 89 |  |  |  |  |  |  | b ::= 'frac' | 'root' | 'stackrel' | 'newcommand' | 'newsymbol' | 
| 90 |  |  |  |  |  |  | binary symbols | 
| 91 |  |  |  |  |  |  | l ::= ( | [ | { | (: | {:          left brackets | 
| 92 |  |  |  |  |  |  | r ::= ) | ] | } | :) | :}          right brackets | 
| 93 |  |  |  |  |  |  | S ::= c | lEr | uS | bSS | "any"   simple expression | 
| 94 |  |  |  |  |  |  | E ::= SE | S/S |S_S | S^S | S_S^S  expression (fraction, sub-, | 
| 95 |  |  |  |  |  |  | super-, subsuperscript) | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | =back | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  | =head3 The translation rules | 
| 100 |  |  |  |  |  |  |  | 
| 101 |  |  |  |  |  |  | Each terminal symbol is translated into a corresponding MathML | 
| 102 |  |  |  |  |  |  | node. The constants are mostly converted to their respective Unicode | 
| 103 |  |  |  |  |  |  | symbols. The other expressions are converted as follows: | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | =over 4 | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | lSr	  ->	lSr | 
| 108 |  |  |  |  |  |  | (note that any pair of brackets can be used to | 
| 109 |  |  |  |  |  |  | delimit subexpressions, they don't have to match) | 
| 110 |  |  |  |  |  |  | sqrt S	  ->	S' | 
| 111 |  |  |  |  |  |  | text S	  ->	S' | 
| 112 |  |  |  |  |  |  | "any"	  ->	any | 
| 113 |  |  |  |  |  |  | frac S1 S2	->	S1' S2' | 
| 114 |  |  |  |  |  |  | root S1 S2	->	S2' S1' | 
| 115 |  |  |  |  |  |  | stackrel S1 S2	->	S2' S1' | 
| 116 |  |  |  |  |  |  | S1/S2	  ->	S1' S2' | 
| 117 |  |  |  |  |  |  | S1_S2	  ->	S1 S2' | 
| 118 |  |  |  |  |  |  | S1^S2	  ->	S1 S2' | 
| 119 |  |  |  |  |  |  | S1_S2^S3 ->	S1 S2' S3' or | 
| 120 |  |  |  |  |  |  | S1 S2' S3' (in some cases) | 
| 121 |  |  |  |  |  |  | S1^S2_S3 ->	S1 S3' S2' or | 
| 122 |  |  |  |  |  |  | S1 S3' S2' (in some cases) | 
| 123 |  |  |  |  |  |  |  | 
| 124 |  |  |  |  |  |  | =back | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | In the rules above, the expression C  is the same as C, except that if  | 
| 127 |  |  |  |  |  |  | C  has an outer level of brackets, then C is the expression inside   | 
| 128 |  |  |  |  |  |  | these brackets. | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | =head3 Matrices | 
| 131 |  |  |  |  |  |  |  | 
| 132 |  |  |  |  |  |  | A simple syntax for matrices is also recognized: | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | l(S11,...,S1n),(...),(Sm1,...,Smn)r | 
| 135 |  |  |  |  |  |  | or | 
| 136 |  |  |  |  |  |  | l[S11,...,S1n],[...],[Sm1,...,Smn]r. | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | Here C and C stand for any of the left and right | 
| 139 |  |  |  |  |  |  | brackets (just like in the grammar they do not have to match). Both of | 
| 140 |  |  |  |  |  |  | these expressions are translated to | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | lS11... | 
| 143 |  |  |  |  |  |  | S1n... | 
| 144 |  |  |  |  |  |  | Sm1... | 
| 145 |  |  |  |  |  |  | Smnr. | 
| 146 |  |  |  |  |  |  |  | 
| 147 |  |  |  |  |  |  | Note that each row must have the same number of expressions, and there | 
| 148 |  |  |  |  |  |  | should be at least two rows. | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | LaTeX matrix commands are not recognized. | 
| 151 |  |  |  |  |  |  |  | 
| 152 |  |  |  |  |  |  | =head3 Tokenization | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | The input formula is broken into tokens using a "longest matching | 
| 155 |  |  |  |  |  |  | initial substring search". Suppose the input formula has been | 
| 156 |  |  |  |  |  |  | processed from left to right up to a fixed position. The longest | 
| 157 |  |  |  |  |  |  | string from the list of constants (given below) that matches the | 
| 158 |  |  |  |  |  |  | initial part of the remainder of the formula is the next token. If | 
| 159 |  |  |  |  |  |  | there is no matching string, then the first character of the remainder | 
| 160 |  |  |  |  |  |  | is the next token.  The symbol table at the top of the ASCIIMathML.js | 
| 161 |  |  |  |  |  |  | script specifies whether a symbol is a math operator (surrounded by a | 
| 162 |  |  |  |  |  |  | C<<  >> tag) or a math identifier (surrounded by a C<<  >> | 
| 163 |  |  |  |  |  |  | tag). For single character tokens, letters are treated as math | 
| 164 |  |  |  |  |  |  | identifiers, and non-alphanumeric characters are treated as math | 
| 165 |  |  |  |  |  |  | operators. For digits, see "Numbers" below. | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | Spaces are significant when they separate characters and thus prevent | 
| 168 |  |  |  |  |  |  | a certain string of characters from matching one of the | 
| 169 |  |  |  |  |  |  | constants. Multiple spaces and end-of-line characters are equivalent | 
| 170 |  |  |  |  |  |  | to a single space. | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | =head3 Numbers | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | A string of digits, optionally followed by a decimal point (a period) | 
| 175 |  |  |  |  |  |  | and another string of digits, is parsed as a single token and | 
| 176 |  |  |  |  |  |  | converted to a MathML number, i.e., enclosed with the C<<  >> | 
| 177 |  |  |  |  |  |  | tag. | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | =head3 Greek letters | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | =over 4 | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | =item Lowercase letters | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | C C C C C C C C | 
| 186 |  |  |  |  |  |  | C C C C C C C C C | 
| 187 |  |  |  |  |  |  | C C C C C C | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | =item Uppercase letters | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | C C C C C C C C | 
| 192 |  |  |  |  |  |  | C C | 
| 193 |  |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  | =item Variants | 
| 195 |  |  |  |  |  |  |  | 
| 196 |  |  |  |  |  |  | C C C | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | =back | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  | =head3 Standard functions | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | sin cos tan csc sec cot sinh cosh tanh log ln det dim lim mod gcd lcm | 
| 203 |  |  |  |  |  |  | min max | 
| 204 |  |  |  |  |  |  |  | 
| 205 |  |  |  |  |  |  | =head3 Operation symbols | 
| 206 |  |  |  |  |  |  |  | 
| 207 |  |  |  |  |  |  | Type	  Description					Entity | 
| 208 |  |  |  |  |  |  | +	  +						+ | 
| 209 |  |  |  |  |  |  | -	  -						- | 
| 210 |  |  |  |  |  |  | *	  Mid dot					⋅ | 
| 211 |  |  |  |  |  |  | **	  Star						⋆ | 
| 212 |  |  |  |  |  |  | //	  /						/ | 
| 213 |  |  |  |  |  |  | \\	  \						\ | 
| 214 |  |  |  |  |  |  | xx	  Cross product					× | 
| 215 |  |  |  |  |  |  | -:	  Divided by					÷ | 
| 216 |  |  |  |  |  |  | @	  Compose functions				∘ | 
| 217 |  |  |  |  |  |  | o+	  Circle with plus 				⊕ | 
| 218 |  |  |  |  |  |  | ox	  Circle with x					⊗ | 
| 219 |  |  |  |  |  |  | o.	  Circle with dot				⊙ | 
| 220 |  |  |  |  |  |  | sum	  Sum for sub- and superscript			∑ | 
| 221 |  |  |  |  |  |  | prod	  Product for sub- and superscript		∏ | 
| 222 |  |  |  |  |  |  | ^^	  Logic "and"					∧ | 
| 223 |  |  |  |  |  |  | ^^^	  Logic "and" for sub- and superscript 		⋀ | 
| 224 |  |  |  |  |  |  | vv	  Logic "or"					∨ | 
| 225 |  |  |  |  |  |  | vvv	  Logic "or" for sub- and superscript		⋁ | 
| 226 |  |  |  |  |  |  | nn	  Logic "intersect"				∩ | 
| 227 |  |  |  |  |  |  | nnn	  Logic "intersect" for sub- and superscript	⋂ | 
| 228 |  |  |  |  |  |  | uu	  Logic "union"					∪ | 
| 229 |  |  |  |  |  |  | uuu	  Logic "union" for sub- and superscript	⋃ | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  |  |  |  | =head3 Relation symbols | 
| 232 |  |  |  |  |  |  |  | 
| 233 |  |  |  |  |  |  | Type	  Description 					Entity | 
| 234 |  |  |  |  |  |  | =	  =						= | 
| 235 |  |  |  |  |  |  | !=	  Not equals					≠ | 
| 236 |  |  |  |  |  |  | <	  <						< | 
| 237 |  |  |  |  |  |  | >	  >						> | 
| 238 |  |  |  |  |  |  | <=	  Less than or equal				≤ | 
| 239 |  |  |  |  |  |  | >=	  Greater than or equal				≥ | 
| 240 |  |  |  |  |  |  | -lt	  Precedes					≺ | 
| 241 |  |  |  |  |  |  | >-	  Succeeds					≻ | 
| 242 |  |  |  |  |  |  | in	  Element of					∈ | 
| 243 |  |  |  |  |  |  | !in	  Not an element of				∉ | 
| 244 |  |  |  |  |  |  | sub	  Subset					⊂ | 
| 245 |  |  |  |  |  |  | sup	  Superset					⊃ | 
| 246 |  |  |  |  |  |  | sube	  Subset or equal				⊆ | 
| 247 |  |  |  |  |  |  | supe	  Superset or equal				⊇ | 
| 248 |  |  |  |  |  |  | -=	  Equivalent					≡ | 
| 249 |  |  |  |  |  |  | ~=	  Congruent to					≅ | 
| 250 |  |  |  |  |  |  | ~~	  Asymptotically equal to			≈ | 
| 251 |  |  |  |  |  |  | prop	  Proportional to				∝ | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | =head3 Logical symbols | 
| 254 |  |  |  |  |  |  |  | 
| 255 |  |  |  |  |  |  | Type	  Description 					Entity | 
| 256 |  |  |  |  |  |  | and	  And						" and " | 
| 257 |  |  |  |  |  |  | or	  Or						" or " | 
| 258 |  |  |  |  |  |  | not	  Not						¬ | 
| 259 |  |  |  |  |  |  | =>	  Implies					⇒ | 
| 260 |  |  |  |  |  |  | if	  If						" if " | 
| 261 |  |  |  |  |  |  | iff	  If and only if				⇔ | 
| 262 |  |  |  |  |  |  | AA	  For all					∀ | 
| 263 |  |  |  |  |  |  | EE	  There exists					∃ | 
| 264 |  |  |  |  |  |  | _|_	  Perpendicular, bottom				⊥ | 
| 265 |  |  |  |  |  |  | TT	  Top						⊤ | 
| 266 |  |  |  |  |  |  | |--	  Right tee					⊢ | 
| 267 |  |  |  |  |  |  | |==	  Double right tee				⊨ | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | =head3 Grouping brackets | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | Type	  Description 					Entity | 
| 272 |  |  |  |  |  |  | (	  (						( | 
| 273 |  |  |  |  |  |  | )	  )						) | 
| 274 |  |  |  |  |  |  | [	  [						[ | 
| 275 |  |  |  |  |  |  | ]	  ]						] | 
| 276 |  |  |  |  |  |  | {	  {						{ | 
| 277 |  |  |  |  |  |  | }	  }						} | 
| 278 |  |  |  |  |  |  | (:	  Left angle bracket				〈 | 
| 279 |  |  |  |  |  |  | :)	  Right angle bracket				〉 | 
| 280 |  |  |  |  |  |  | {:	  Invisible left grouping element | 
| 281 |  |  |  |  |  |  | :}	  Invisible right grouping element | 
| 282 |  |  |  |  |  |  |  | 
| 283 |  |  |  |  |  |  | =head3 Miscellaneous symbols | 
| 284 |  |  |  |  |  |  |  | 
| 285 |  |  |  |  |  |  | Type	  Description 					Entity | 
| 286 |  |  |  |  |  |  | int	  Integral					∫ | 
| 287 |  |  |  |  |  |  | oint	  Countour integral				∮ | 
| 288 |  |  |  |  |  |  | del	  Partial derivative				&del; | 
| 289 |  |  |  |  |  |  | grad	  Gradient					∇ | 
| 290 |  |  |  |  |  |  | +-	  Plus or minus					± | 
| 291 |  |  |  |  |  |  | O/	  Null set					∅ | 
| 292 |  |  |  |  |  |  | oo       Infinity					∞ | 
| 293 |  |  |  |  |  |  | aleph	  Hebrew letter aleph				ℵ | 
| 294 |  |  |  |  |  |  | &        Ampersand					& | 
| 295 |  |  |  |  |  |  | /_	  Angle						∠ | 
| 296 |  |  |  |  |  |  | :.	  Therefore					∴ | 
| 297 |  |  |  |  |  |  | ...	  Ellipsis					... | 
| 298 |  |  |  |  |  |  | cdots	  Three centered dots				⋯ | 
| 299 |  |  |  |  |  |  | \    Non-breaking space ( means space) | 
| 300 |  |  |  |  |  |  | quad	  Quad space | 
| 301 |  |  |  |  |  |  | diamond  Diamond					⋄ | 
| 302 |  |  |  |  |  |  | square	  Square					□ | 
| 303 |  |  |  |  |  |  | |__	  Left floor					⌊ | 
| 304 |  |  |  |  |  |  | __|	  Right floor					⌋ | 
| 305 |  |  |  |  |  |  | |~	  Left ceiling					⌈ | 
| 306 |  |  |  |  |  |  | ~|	  Right ceiling					⌉ | 
| 307 |  |  |  |  |  |  | CC	  Complex numbers				ℂ | 
| 308 |  |  |  |  |  |  | NN	  Natural numbers				ℕ | 
| 309 |  |  |  |  |  |  | QQ	  Rational numbers				ℚ | 
| 310 |  |  |  |  |  |  | RR	  Real numbers					ℝ | 
| 311 |  |  |  |  |  |  | ZZ	  Integers					ℤ | 
| 312 |  |  |  |  |  |  |  | 
| 313 |  |  |  |  |  |  | =head3 Arrows | 
| 314 |  |  |  |  |  |  |  | 
| 315 |  |  |  |  |  |  | Type	  Description 					Entity | 
| 316 |  |  |  |  |  |  | uarr	  Up arrow					↑ | 
| 317 |  |  |  |  |  |  | darr	  Down arrow					↓ | 
| 318 |  |  |  |  |  |  | rarr	  Right arrow					→ | 
| 319 |  |  |  |  |  |  | ->	  Right arrow					→ | 
| 320 |  |  |  |  |  |  | larr	  Left arrow					← | 
| 321 |  |  |  |  |  |  | harr     Horizontal (two-way) arrow			↔ | 
| 322 |  |  |  |  |  |  | rArr	  Right double arrow				⇒ | 
| 323 |  |  |  |  |  |  | lArr	  Left double arrow				⇐ | 
| 324 |  |  |  |  |  |  | hArr	  Horizontal double arrow			⇔ | 
| 325 |  |  |  |  |  |  |  | 
| 326 |  |  |  |  |  |  | =head3 Accents | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | Type	 Description	     Output | 
| 329 |  |  |  |  |  |  | hat x	 Hat over x	     x^ | 
| 330 |  |  |  |  |  |  | bar x	 Bar over x	     x¯ | 
| 331 |  |  |  |  |  |  | ul x	 Underbar under x    x_ | 
| 332 |  |  |  |  |  |  | vec x	 Right arrow over x  x→ | 
| 333 |  |  |  |  |  |  | dot x	 Dot over x	     x. | 
| 334 |  |  |  |  |  |  | ddot x	 Double dot over x   x.. | 
| 335 |  |  |  |  |  |  |  | 
| 336 |  |  |  |  |  |  | =head3 Font commands | 
| 337 |  |  |  |  |  |  |  | 
| 338 |  |  |  |  |  |  | Type	  Description | 
| 339 |  |  |  |  |  |  | bb A	  Bold A | 
| 340 |  |  |  |  |  |  | bbb A	  Double-struck A | 
| 341 |  |  |  |  |  |  | cc A	  Calligraphic (script) A | 
| 342 |  |  |  |  |  |  | tt A	  Teletype (monospace) A | 
| 343 |  |  |  |  |  |  | fr A	  Fraktur A | 
| 344 |  |  |  |  |  |  | sf A	  Sans-serif A | 
| 345 |  |  |  |  |  |  |  | 
| 346 |  |  |  |  |  |  | =head3 Defining new commands and symbols | 
| 347 |  |  |  |  |  |  |  | 
| 348 |  |  |  |  |  |  | It is possible to define new commands and symbols using the | 
| 349 |  |  |  |  |  |  | 'newcommand' and 'newsymbol' binary operators.  The former defines a | 
| 350 |  |  |  |  |  |  | macro that gets expanded and reparsed as ASCIIMathML and the latter | 
| 351 |  |  |  |  |  |  | defines a constant that gets used as a math operator (C<<  >>) | 
| 352 |  |  |  |  |  |  | element.  Both of the arguments must be text, optionally enclosed in | 
| 353 |  |  |  |  |  |  | grouping operators.  The 'newsymbol' operator also allows the | 
| 354 |  |  |  |  |  |  | second argument to be a group of two text strings where the first is | 
| 355 |  |  |  |  |  |  | the mathml operator and the second is the latex code to be output. | 
| 356 |  |  |  |  |  |  |  | 
| 357 |  |  |  |  |  |  | For example, 'newcommand "DDX" "{:d/dx:}"' would define a new command | 
| 358 |  |  |  |  |  |  | 'DDX'.  It could then be invoked like 'DDXf(x)', which would | 
| 359 |  |  |  |  |  |  | expand to '{:d/dx:}f(x)'.  The text 'newsymbol{"!le"}{"≰"}' | 
| 360 |  |  |  |  |  |  | could be used to create a symbol you could invoke with '!le', as in 'a | 
| 361 |  |  |  |  |  |  | !le b'. | 
| 362 |  |  |  |  |  |  |  | 
| 363 |  |  |  |  |  |  | =head2 Attributes for | 
| 364 |  |  |  |  |  |  |  | 
| 365 |  |  |  |  |  |  | =over 4 | 
| 366 |  |  |  |  |  |  |  | 
| 367 |  |  |  |  |  |  | =item C | 
| 368 |  |  |  |  |  |  |  | 
| 369 |  |  |  |  |  |  | The title attribute for the element, if specified.  In many browsers, | 
| 370 |  |  |  |  |  |  | this string will appear if you hover over the MathML markup. | 
| 371 |  |  |  |  |  |  |  | 
| 372 |  |  |  |  |  |  | =item C | 
| 373 |  |  |  |  |  |  |  | 
| 374 |  |  |  |  |  |  | The id attribute for the element, if specified. | 
| 375 |  |  |  |  |  |  |  | 
| 376 |  |  |  |  |  |  | =item C | 
| 377 |  |  |  |  |  |  |  | 
| 378 |  |  |  |  |  |  | The class attribute for the element, if specified. | 
| 379 |  |  |  |  |  |  |  | 
| 380 |  |  |  |  |  |  | =back | 
| 381 |  |  |  |  |  |  |  | 
| 382 |  |  |  |  |  |  | =head2 Attributes for | 
| 383 |  |  |  |  |  |  |  | 
| 384 |  |  |  |  |  |  | =over 4 | 
| 385 |  |  |  |  |  |  |  | 
| 386 |  |  |  |  |  |  | =item C | 
| 387 |  |  |  |  |  |  |  | 
| 388 |  |  |  |  |  |  | The displaystyle attribute for the element, if specified.  One of the | 
| 389 |  |  |  |  |  |  | values "true" or "false".  If the displaystyle is false, then fractions | 
| 390 |  |  |  |  |  |  | are represented with a smaller font size and the placement of | 
| 391 |  |  |  |  |  |  | subscripts and superscripts of sums and integrals changes. | 
| 392 |  |  |  |  |  |  |  | 
| 393 |  |  |  |  |  |  | =item C | 
| 394 |  |  |  |  |  |  |  | 
| 395 |  |  |  |  |  |  | The mathvariant attribute for the element, if specified. One of the | 
| 396 |  |  |  |  |  |  | values "normal", "bold", "italic", "bold-italic", "double-struck", | 
| 397 |  |  |  |  |  |  | "bold-fraktur", "script", "bold-script", "fraktur", "sans-serif", | 
| 398 |  |  |  |  |  |  | "bold-sans-serif", "sans-serif-italic", "sans-serif-bold-italic", or | 
| 399 |  |  |  |  |  |  | "monospace". | 
| 400 |  |  |  |  |  |  |  | 
| 401 |  |  |  |  |  |  | =item C | 
| 402 |  |  |  |  |  |  |  | 
| 403 |  |  |  |  |  |  | The mathsize attribute for the element, if specified. Either "small", | 
| 404 |  |  |  |  |  |  | "normal" or "big", or of the form "number v-unit". | 
| 405 |  |  |  |  |  |  |  | 
| 406 |  |  |  |  |  |  | =item C | 
| 407 |  |  |  |  |  |  |  | 
| 408 |  |  |  |  |  |  | A string representing the font family. | 
| 409 |  |  |  |  |  |  |  | 
| 410 |  |  |  |  |  |  | =item C | 
| 411 |  |  |  |  |  |  |  | 
| 412 |  |  |  |  |  |  | The mathcolor attribute for the element, if specified. It be in one of | 
| 413 |  |  |  |  |  |  | the forms "#rgb" or "#rrggbb", or should be an html-color-name. | 
| 414 |  |  |  |  |  |  |  | 
| 415 |  |  |  |  |  |  | =item C | 
| 416 |  |  |  |  |  |  |  | 
| 417 |  |  |  |  |  |  | The mathbackground attribute for the element, if specified. It should | 
| 418 |  |  |  |  |  |  | be in one of the forms "#rgb" or "#rrggbb", or an html-color-name, or | 
| 419 |  |  |  |  |  |  | the keyword "transparent". | 
| 420 |  |  |  |  |  |  |  | 
| 421 |  |  |  |  |  |  |  | 
| 422 |  |  |  |  |  |  | =head1 METHODS | 
| 423 |  |  |  |  |  |  |  | 
| 424 |  |  |  |  |  |  | =head2 C | 
| 425 |  |  |  |  |  |  |  | 
| 426 |  |  |  |  |  |  | =head3 C | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | Converts C<$text> to a MathML string. If the optional C<$math_attr> | 
| 429 |  |  |  |  |  |  | argument is provided, it should be a reference to a hash of | 
| 430 |  |  |  |  |  |  | attribute/value pairs for the C< | 
| 431 |  |  |  |  |  |  | C<$mstyle_attr> argument is provided, it should be a reference to a | 
| 432 |  |  |  |  |  |  | hash of attribute/value pairs for the C<  > node. | 
| 433 |  |  |  |  |  |  |  | 
| 434 |  |  |  |  |  |  | =head3 C | 
| 435 |  |  |  |  |  |  |  | 
| 436 |  |  |  |  |  |  | Like C except that instead of returning a string, it | 
| 437 |  |  |  |  |  |  | returns a C representing the parsed MathML | 
| 438 |  |  |  |  |  |  | structure. | 
| 439 |  |  |  |  |  |  |  | 
| 440 |  |  |  |  |  |  | =head2 C | 
| 441 |  |  |  |  |  |  |  | 
| 442 |  |  |  |  |  |  | =head3 C | 
| 443 |  |  |  |  |  |  |  | 
| 444 |  |  |  |  |  |  | Returns a MathML string representing the parsed MathML structure | 
| 445 |  |  |  |  |  |  | encoded by the C. | 
| 446 |  |  |  |  |  |  |  | 
| 447 |  |  |  |  |  |  | =head3 C | 
| 448 |  |  |  |  |  |  |  | 
| 449 |  |  |  |  |  |  | Returns a LaTeX string representing the parsed MathML structure | 
| 450 |  |  |  |  |  |  | encoded by the C. | 
| 451 |  |  |  |  |  |  |  | 
| 452 |  |  |  |  |  |  | =head1 BUGS AND SUGGESTIONS | 
| 453 |  |  |  |  |  |  |  | 
| 454 |  |  |  |  |  |  | If you find bugs, think of anything that could improve Text::ASCIIMathML | 
| 455 |  |  |  |  |  |  | or have any questions related to it, feel free to contact the author. | 
| 456 |  |  |  |  |  |  |  | 
| 457 |  |  |  |  |  |  | =head1 AUTHOR | 
| 458 |  |  |  |  |  |  |  | 
| 459 |  |  |  |  |  |  | Mark Nodine | 
| 460 |  |  |  |  |  |  |  | 
| 461 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 462 |  |  |  |  |  |  |  | 
| 463 |  |  |  |  |  |  | MathML::Entities, | 
| 464 |  |  |  |  |  |  |  | 
| 465 |  |  |  |  |  |  |  | 
| 466 |  |  |  |  |  |  | =head1 ACKNOWLEDGEMENTS | 
| 467 |  |  |  |  |  |  |  | 
| 468 |  |  |  |  |  |  | This Perl module has been created by modifying Peter Jipsen's | 
| 469 |  |  |  |  |  |  | ASCIIMathML.js script. He deserves full credit for the original | 
| 470 |  |  |  |  |  |  | implementation; any bugs have probably been introduced by me. | 
| 471 |  |  |  |  |  |  |  | 
| 472 |  |  |  |  |  |  | =head1 COPYRIGHT | 
| 473 |  |  |  |  |  |  |  | 
| 474 |  |  |  |  |  |  | The Text::ASCIIMathML module is copyright (c) 2006 Mark Nodine, | 
| 475 |  |  |  |  |  |  | USA. All rights reserved. | 
| 476 |  |  |  |  |  |  |  | 
| 477 |  |  |  |  |  |  | You may use and distribute them under the terms of either the GNU | 
| 478 |  |  |  |  |  |  | General Public License or the Artistic License, as specified in the | 
| 479 |  |  |  |  |  |  | Perl README file. | 
| 480 |  |  |  |  |  |  |  | 
| 481 |  |  |  |  |  |  | =cut | 
| 482 |  |  |  |  |  |  |  | 
| 483 | 1 |  |  | 1 |  | 47331 | use strict; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 39 |  | 
| 484 | 1 |  |  | 1 |  | 6 | use warnings; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 3808 |  | 
| 485 |  |  |  |  |  |  |  | 
| 486 |  |  |  |  |  |  | our $VERSION = '0.81'; | 
| 487 |  |  |  |  |  |  |  | 
| 488 |  |  |  |  |  |  | # Creates a new Text::ASCIIMathML parser object | 
| 489 |  |  |  |  |  |  | sub new { | 
| 490 | 1 |  |  | 1 | 1 | 11230 | my ($class) = @_; | 
| 491 | 1 |  |  |  |  | 5 | return bless {}, $class; | 
| 492 |  |  |  |  |  |  | } | 
| 493 |  |  |  |  |  |  |  | 
| 494 |  |  |  |  |  |  | # Sets an attribute to a given value | 
| 495 |  |  |  |  |  |  | # Arguments: Attribute name, attribute value | 
| 496 |  |  |  |  |  |  | # Returns:   None | 
| 497 |  |  |  |  |  |  | # Supported attributes: | 
| 498 |  |  |  |  |  |  | #            ForMoz       Boolean to optimize for Netscape/Mozilla/Firefox | 
| 499 |  |  |  |  |  |  | sub SetAttribute : method { | 
| 500 | 1 |  |  | 1 | 0 | 7 | my ($self, $attr, $val) = @_; | 
| 501 | 1 |  |  |  |  | 11 | $self->{attr}{$attr} = $val; | 
| 502 |  |  |  |  |  |  | } | 
| 503 |  |  |  |  |  |  |  | 
| 504 |  |  |  |  |  |  | # Converts an AsciiMathML string to a MathML one | 
| 505 |  |  |  |  |  |  | # Arguments: AsciiMathML string, | 
| 506 |  |  |  |  |  |  | #            optional ref to array of attribute/value pairs for math node, | 
| 507 |  |  |  |  |  |  | #            optional ref to array of attribute/value pairs for mstyle node | 
| 508 |  |  |  |  |  |  | # Returns:   MathML string | 
| 509 |  |  |  |  |  |  | sub TextToMathML : method { | 
| 510 | 275 |  |  | 275 | 1 | 289252 | my $tree = TextToMathMLTree(@_); | 
| 511 | 275 | 100 |  |  |  | 1134 | return $tree ? $tree->text : ''; | 
| 512 |  |  |  |  |  |  | } | 
| 513 |  |  |  |  |  |  |  | 
| 514 |  |  |  |  |  |  | # Converts an AsciiMathML string to a tree of MathML nodes | 
| 515 |  |  |  |  |  |  | # Arguments: AsciiMathML string, | 
| 516 |  |  |  |  |  |  | #            optional ref to array of attribute/value pairs for math node, | 
| 517 |  |  |  |  |  |  | #            optional ref to array of attribute/value pairs for mstyle node | 
| 518 |  |  |  |  |  |  | # Returns:   top Text::ASCIIMathML::Node object or undefined | 
| 519 |  |  |  |  |  |  | sub TextToMathMLTree : method { | 
| 520 | 548 |  |  | 548 | 1 | 1196 | my ($self, $expr, $mathAttr, $mstyleAttr) = @_; | 
| 521 | 548 | 50 |  |  |  | 20657 | $expr = '' unless defined $expr; | 
| 522 | 548 |  |  |  |  | 5688 | my $mstyle = $self->_createElementMathML('mstyle'); | 
| 523 | 548 | 50 |  |  |  | 3853 | $mstyle->setAttribute | 
|  |  | 50 |  |  |  |  |  | 
| 524 |  |  |  |  |  |  | (ref $mstyleAttr eq 'ARRAY' ? @$mstyleAttr : %$mstyleAttr) | 
| 525 |  |  |  |  |  |  | if $mstyleAttr; | 
| 526 | 548 |  |  |  |  | 1007 | $self->{nestingDepth} = 0; | 
| 527 | 548 |  |  |  |  | 1376 | $expr =~ s/^\s+//; | 
| 528 | 548 |  |  |  |  | 1997 | $mstyle->appendChild(($self->_parseExpr($expr, 0))[0]); | 
| 529 | 548 | 100 |  |  |  | 2415 | return unless $mstyle->childNodes > 0; | 
| 530 | 547 |  |  |  |  | 1624 | my $math = $self->_createMmlNode('math', $mstyle); | 
| 531 | 547 |  |  |  |  | 1340 | $expr =~ s/\n\s*//g; | 
| 532 | 547 | 50 |  |  |  | 2163 | $math->setAttribute(ref $mathAttr eq 'ARRAY' ? @$mathAttr : %$mathAttr) | 
|  |  | 100 |  |  |  |  |  | 
| 533 |  |  |  |  |  |  | if $mathAttr; | 
| 534 |  |  |  |  |  |  |  | 
| 535 | 547 |  |  |  |  | 2110 | return $math; | 
| 536 |  |  |  |  |  |  | } | 
| 537 |  |  |  |  |  |  |  | 
| 538 |  |  |  |  |  |  |  | 
| 539 |  |  |  |  |  |  |  | 
| 540 |  |  |  |  |  |  | # Creates an Text::ASCIIMathML::Node object with no tag | 
| 541 |  |  |  |  |  |  | # Arguments: None | 
| 542 |  |  |  |  |  |  | # Returns:   node object | 
| 543 |  |  |  |  |  |  | sub _createDocumentFragment : method { | 
| 544 | 4069 |  |  | 4069 |  | 5304 | my ($self) = @_; | 
| 545 | 4069 |  |  |  |  | 10489 | return Text::ASCIIMathML::Node->new($self); | 
| 546 |  |  |  |  |  |  | } | 
| 547 |  |  |  |  |  |  |  | 
| 548 |  |  |  |  |  |  | # Creates an Text::ASCIIMathML::Node object | 
| 549 |  |  |  |  |  |  | # Arguments: tag | 
| 550 |  |  |  |  |  |  | # Returns:   node object | 
| 551 |  |  |  |  |  |  | sub _createElementMathML : method { | 
| 552 | 586 |  |  | 586 |  | 1285 | my ($self, $t) = @_; | 
| 553 | 586 |  |  |  |  | 1977 | return Text::ASCIIMathML::Node->new($self, $t); | 
| 554 |  |  |  |  |  |  | } | 
| 555 |  |  |  |  |  |  |  | 
| 556 |  |  |  |  |  |  | # Creates an Text::ASCIIMathML::Node object and appends a node as a child | 
| 557 |  |  |  |  |  |  | # Arguments: tag, node | 
| 558 |  |  |  |  |  |  | # Returns:   node object | 
| 559 |  |  |  |  |  |  | sub _createMmlNode : method { | 
| 560 | 4880 |  |  | 4880 |  | 10078 | my ($self, $t, $obj) = @_; | 
| 561 | 4880 |  |  |  |  | 14713 | my $node = Text::ASCIIMathML::Node->new($self, $t); | 
| 562 | 4880 |  |  |  |  | 12341 | $node->appendChild($obj); | 
| 563 | 4880 |  |  |  |  | 41189 | return $node; | 
| 564 |  |  |  |  |  |  | } | 
| 565 |  |  |  |  |  |  |  | 
| 566 |  |  |  |  |  |  | # Creates an Text::ASCIIMathML::Node text object with the given text | 
| 567 |  |  |  |  |  |  | # Arguments: text | 
| 568 |  |  |  |  |  |  | # Returns:   node object | 
| 569 |  |  |  |  |  |  | sub _createTextNode : method { | 
| 570 | 3193 |  |  | 3193 |  | 5932 | my ($self, $text) = @_; | 
| 571 | 3193 |  |  |  |  | 12802 | return Text::ASCIIMathML::Node->newText ($self, $text); | 
| 572 |  |  |  |  |  |  | } | 
| 573 |  |  |  |  |  |  |  | 
| 574 |  |  |  |  |  |  | # Finds maximal initial substring of str that appears in names | 
| 575 |  |  |  |  |  |  | # return null if there is none | 
| 576 |  |  |  |  |  |  | # Arguments: string | 
| 577 |  |  |  |  |  |  | # Returns:   matched input, entry from AMSymbol (if any) | 
| 578 |  |  |  |  |  |  | sub _getSymbol : method { | 
| 579 | 10482 |  |  | 10482 |  | 15796 | my $self = shift; | 
| 580 | 10482 |  |  |  |  | 23860 | my ($input, $symbol) = $self->_getSymbol_(@_); | 
| 581 | 10482 | 100 |  |  |  | 37063 | $self->{previousSymbol} = $symbol->{ttype} if $symbol; | 
| 582 | 10482 |  |  |  |  | 40184 | return $input, $symbol; | 
| 583 |  |  |  |  |  |  | } | 
| 584 |  |  |  |  |  |  |  | 
| 585 |  |  |  |  |  |  | BEGIN { | 
| 586 |  |  |  |  |  |  | # character lists for Mozilla/Netscape fonts | 
| 587 | 1 |  |  | 1 |  | 7 | my $AMcal = [0xEF35,0x212C,0xEF36,0xEF37,0x2130,0x2131,0xEF38,0x210B,0x2110,0xEF39,0xEF3A,0x2112,0x2133,0xEF3B,0xEF3C,0xEF3D,0xEF3E,0x211B,0xEF3F,0xEF40,0xEF41,0xEF42,0xEF43,0xEF44,0xEF45,0xEF46]; | 
| 588 | 1 |  |  |  |  | 9 | my $AMfrk = [0xEF5D,0xEF5E,0x212D,0xEF5F,0xEF60,0xEF61,0xEF62,0x210C,0x2111,0xEF63,0xEF64,0xEF65,0xEF66,0xEF67,0xEF68,0xEF69,0xEF6A,0x211C,0xEF6B,0xEF6C,0xEF6D,0xEF6E,0xEF6F,0xEF70,0xEF71,0x2128]; | 
| 589 | 1 |  |  |  |  | 4 | my $AMbbb = [0xEF8C,0xEF8D,0x2102,0xEF8E,0xEF8F,0xEF90,0xEF91,0x210D,0xEF92,0xEF93,0xEF94,0xEF95,0xEF96,0x2115,0xEF97,0x2119,0x211A,0x211D,0xEF98,0xEF99,0xEF9A,0xEF9B,0xEF9C,0xEF9D,0xEF9E,0x2124]; | 
| 590 |  |  |  |  |  |  |  | 
| 591 |  |  |  |  |  |  | # Create closure for static variables | 
| 592 | 1 |  |  |  |  | 652 | my %AMSymbol = ( | 
| 593 |  |  |  |  |  |  | "sqrt" => { tag=>"msqrt", output=>"sqrt", tex=>'', ttype=>"UNARY" }, | 
| 594 |  |  |  |  |  |  | "root" => { tag=>"mroot", output=>"root", tex=>'', ttype=>"BINARY" }, | 
| 595 |  |  |  |  |  |  | "frac" => { tag=>"mfrac", output=>"/",    tex=>'', ttype=>"BINARY" }, | 
| 596 |  |  |  |  |  |  | "/" => { tag=>"mfrac", output=>"/",    tex=>'', ttype=>"INFIX" }, | 
| 597 |  |  |  |  |  |  | "stackrel" => { tag=>"mover", output=>"stackrel", tex=>'', ttype=>"BINARY" }, | 
| 598 |  |  |  |  |  |  | "_" => { tag=>"msub",  output=>"_",    tex=>'', ttype=>"INFIX" }, | 
| 599 |  |  |  |  |  |  | "^" => { tag=>"msup",  output=>"^",    tex=>'', ttype=>"INFIX" }, | 
| 600 |  |  |  |  |  |  | "text" => { tag=>"mtext", output=>"text", tex=>'', ttype=>"TEXT" }, | 
| 601 |  |  |  |  |  |  | "mbox" => { tag=>"mtext", output=>"mbox", tex=>'', ttype=>"TEXT" }, | 
| 602 |  |  |  |  |  |  | "\"" => { tag=>"mtext", output=>"mbox", tex=>'', ttype=>"TEXT" }, | 
| 603 |  |  |  |  |  |  |  | 
| 604 |  |  |  |  |  |  | # new for perl | 
| 605 |  |  |  |  |  |  | "newcommand" => { ttype=>"BINARY"}, | 
| 606 |  |  |  |  |  |  | "newsymbol" => { ttype=>"BINARY" }, | 
| 607 |  |  |  |  |  |  |  | 
| 608 |  |  |  |  |  |  | # some greek symbols | 
| 609 |  |  |  |  |  |  | "alpha" => { tag=>"mi", output=>"α", tex=>'', ttype=>"CONST" }, | 
| 610 |  |  |  |  |  |  | "beta" => { tag=>"mi", output=>"β", tex=>'', ttype=>"CONST" }, | 
| 611 |  |  |  |  |  |  | "chi" => { tag=>"mi", output=>"χ", tex=>'', ttype=>"CONST" }, | 
| 612 |  |  |  |  |  |  | "delta" => { tag=>"mi", output=>"δ", tex=>'', ttype=>"CONST" }, | 
| 613 |  |  |  |  |  |  | "Delta" => { tag=>"mo", output=>"Δ", tex=>'', ttype=>"CONST" }, | 
| 614 |  |  |  |  |  |  | "epsi" => { tag=>"mi", output=>"ε", tex=>"epsilon", ttype=>"CONST" }, | 
| 615 |  |  |  |  |  |  | "varepsilon" => { tag=>"mi", output=>"ɛ", tex=>'', ttype=>"CONST" }, | 
| 616 |  |  |  |  |  |  | "eta" => { tag=>"mi", output=>"η", tex=>'', ttype=>"CONST" }, | 
| 617 |  |  |  |  |  |  | "gamma" => { tag=>"mi", output=>"γ", tex=>'', ttype=>"CONST" }, | 
| 618 |  |  |  |  |  |  | "Gamma" => { tag=>"mo", output=>"Γ", tex=>'', ttype=>"CONST" }, | 
| 619 |  |  |  |  |  |  | "iota" => { tag=>"mi", output=>"ι", tex=>'', ttype=>"CONST" }, | 
| 620 |  |  |  |  |  |  | "kappa" => { tag=>"mi", output=>"κ", tex=>'', ttype=>"CONST" }, | 
| 621 |  |  |  |  |  |  | "lambda" => { tag=>"mi", output=>"λ", tex=>'', ttype=>"CONST" }, | 
| 622 |  |  |  |  |  |  | "Lambda" => { tag=>"mo", output=>"Λ", tex=>'', ttype=>"CONST" }, | 
| 623 |  |  |  |  |  |  | "mu" => { tag=>"mi", output=>"μ", tex=>'', ttype=>"CONST" }, | 
| 624 |  |  |  |  |  |  | "nu" => { tag=>"mi", output=>"ν", tex=>'', ttype=>"CONST" }, | 
| 625 |  |  |  |  |  |  | "omega" => { tag=>"mi", output=>"ω", tex=>'', ttype=>"CONST" }, | 
| 626 |  |  |  |  |  |  | "Omega" => { tag=>"mo", output=>"Ω", tex=>'', ttype=>"CONST" }, | 
| 627 |  |  |  |  |  |  | "phi" => { tag=>"mi", output=>"ϕ", tex=>'', ttype=>"CONST" }, | 
| 628 |  |  |  |  |  |  | "varphi" => { tag=>"mi", output=>"φ", tex=>'', ttype=>"CONST" }, | 
| 629 |  |  |  |  |  |  | "Phi" => { tag=>"mo", output=>"Φ", tex=>'', ttype=>"CONST" }, | 
| 630 |  |  |  |  |  |  | "pi" => { tag=>"mi", output=>"π", tex=>'', ttype=>"CONST" }, | 
| 631 |  |  |  |  |  |  | "Pi" => { tag=>"mo", output=>"Π", tex=>'', ttype=>"CONST" }, | 
| 632 |  |  |  |  |  |  | "psi" => { tag=>"mi", output=>"ψ", tex=>'', ttype=>"CONST" }, | 
| 633 |  |  |  |  |  |  | "Psi" => { tag=>"mi", output=>"Ψ", tex=>'', ttype=>"CONST" }, | 
| 634 |  |  |  |  |  |  | "rho" => { tag=>"mi", output=>"ρ", tex=>'', ttype=>"CONST" }, | 
| 635 |  |  |  |  |  |  | "sigma" => { tag=>"mi", output=>"σ", tex=>'', ttype=>"CONST" }, | 
| 636 |  |  |  |  |  |  | "Sigma" => { tag=>"mo", output=>"Σ", tex=>'', ttype=>"CONST" }, | 
| 637 |  |  |  |  |  |  | "tau" => { tag=>"mi", output=>"τ", tex=>'', ttype=>"CONST" }, | 
| 638 |  |  |  |  |  |  | "theta" => { tag=>"mi", output=>"θ", tex=>'', ttype=>"CONST" }, | 
| 639 |  |  |  |  |  |  | "vartheta" => { tag=>"mi", output=>"ϑ", tex=>'', ttype=>"CONST" }, | 
| 640 |  |  |  |  |  |  | "Theta" => { tag=>"mo", output=>"Θ", tex=>'', ttype=>"CONST" }, | 
| 641 |  |  |  |  |  |  | "upsilon" => { tag=>"mi", output=>"υ", tex=>'', ttype=>"CONST" }, | 
| 642 |  |  |  |  |  |  | "xi" => { tag=>"mi", output=>"ξ", tex=>'', ttype=>"CONST" }, | 
| 643 |  |  |  |  |  |  | "Xi" => { tag=>"mo", output=>"Ξ", tex=>'', ttype=>"CONST" }, | 
| 644 |  |  |  |  |  |  | "zeta" => { tag=>"mi", output=>"ζ", tex=>'', ttype=>"CONST" }, | 
| 645 |  |  |  |  |  |  |  | 
| 646 |  |  |  |  |  |  | # binary operation symbols | 
| 647 |  |  |  |  |  |  | "*" => { tag=>"mo", output=>"⋅", tex=>"cdot", ttype=>"CONST" }, | 
| 648 |  |  |  |  |  |  | "**" => { tag=>"mo", output=>"⋆", tex=>"star", ttype=>"CONST" }, | 
| 649 |  |  |  |  |  |  | "//" => { tag=>"mo", output=>"/",      tex=>'', ttype=>"CONST" }, | 
| 650 |  |  |  |  |  |  | "\\\\" => { tag=>"mo", output=>"\\",   tex=>"backslash", ttype=>"CONST" }, | 
| 651 |  |  |  |  |  |  | "setminus" => { tag=>"mo", output=>"\\", tex=>'', ttype=>"CONST" }, | 
| 652 |  |  |  |  |  |  | "xx" => { tag=>"mo", output=>"×", tex=>"times", ttype=>"CONST" }, | 
| 653 |  |  |  |  |  |  | "-:" => { tag=>"mo", output=>"÷", tex=>"div", ttype=>"CONST" }, | 
| 654 |  |  |  |  |  |  | "@" => { tag=>"mo", output=>"∘", tex=>"circ", ttype=>"CONST" }, | 
| 655 |  |  |  |  |  |  | "o+" => { tag=>"mo", output=>"⊕", tex=>"oplus", ttype=>"CONST" }, | 
| 656 |  |  |  |  |  |  | "ox" => { tag=>"mo", output=>"⊗", tex=>"otimes", ttype=>"CONST" }, | 
| 657 |  |  |  |  |  |  | "o." => { tag=>"mo", output=>"⊙", tex=>"odot", ttype=>"CONST" }, | 
| 658 |  |  |  |  |  |  | "sum" => { tag=>"mo", output=>"∑", tex=>'', ttype=>"UNDEROVER" }, | 
| 659 |  |  |  |  |  |  | "prod" => { tag=>"mo", output=>"∏", tex=>'', ttype=>"UNDEROVER" }, | 
| 660 |  |  |  |  |  |  | "^^" => { tag=>"mo", output=>"∧", tex=>"wedge", ttype=>"CONST" }, | 
| 661 |  |  |  |  |  |  | "^^^" => { tag=>"mo", output=>"⋀", tex=>"bigwedge", ttype=>"UNDEROVER" }, | 
| 662 |  |  |  |  |  |  | "vv" => { tag=>"mo", output=>"∨", tex=>"vee", ttype=>"CONST" }, | 
| 663 |  |  |  |  |  |  | "vvv" => { tag=>"mo", output=>"⋁", tex=>"bigvee", ttype=>"UNDEROVER" }, | 
| 664 |  |  |  |  |  |  | "nn" => { tag=>"mo", output=>"∩", tex=>"cap", ttype=>"CONST" }, | 
| 665 |  |  |  |  |  |  | "nnn" => { tag=>"mo", output=>"⋂", tex=>"bigcap", ttype=>"UNDEROVER" }, | 
| 666 |  |  |  |  |  |  | "uu" => { tag=>"mo", output=>"∪", tex=>"cup", ttype=>"CONST" }, | 
| 667 |  |  |  |  |  |  | "uuu" => { tag=>"mo", output=>"⋃", tex=>"bigcup", ttype=>"UNDEROVER" }, | 
| 668 |  |  |  |  |  |  |  | 
| 669 |  |  |  |  |  |  | # binary relation symbols | 
| 670 |  |  |  |  |  |  | "!=" => { tag=>"mo", output=>"≠", tex=>"ne", ttype=>"CONST" }, | 
| 671 |  |  |  |  |  |  | ":=" => { tag=>"mo", output=>":=",     tex=>'', ttype=>"CONST" }, | 
| 672 |  |  |  |  |  |  | #"lt" => { tag=>"mo", output=>"<",      tex=>'', ttype=>"CONST" }, | 
| 673 |  |  |  |  |  |  | "lt" => { tag=>"mo", output=>"<",      tex=>'', ttype=>"CONST" }, | 
| 674 |  |  |  |  |  |  | "<=" => { tag=>"mo", output=>"≤", tex=>"le", ttype=>"CONST" }, | 
| 675 |  |  |  |  |  |  | "lt=" => { tag=>"mo", output=>"≤", tex=>"leq", ttype=>"CONST", latex=>1 }, | 
| 676 |  |  |  |  |  |  | ">=" => { tag=>"mo", output=>"≥", tex=>"ge", ttype=>"CONST" }, | 
| 677 |  |  |  |  |  |  | "geq" => { tag=>"mo", output=>"≥", tex=>'', ttype=>"CONST", latex=>1 }, | 
| 678 |  |  |  |  |  |  | "-<" => { tag=>"mo", output=>"≺", tex=>"prec", ttype=>"CONST", latex=>1 }, | 
| 679 |  |  |  |  |  |  | "-lt" => { tag=>"mo", output=>"≺", tex=>'', ttype=>"CONST" }, | 
| 680 |  |  |  |  |  |  | ">-" => { tag=>"mo", output=>"≻", tex=>"succ", ttype=>"CONST" }, | 
| 681 |  |  |  |  |  |  | "in" => { tag=>"mo", output=>"∈", tex=>'', ttype=>"CONST" }, | 
| 682 |  |  |  |  |  |  | "!in" => { tag=>"mo", output=>"∉", tex=>"notin", ttype=>"CONST" }, | 
| 683 |  |  |  |  |  |  | "sub" => { tag=>"mo", output=>"⊂", tex=>"subset", ttype=>"CONST" }, | 
| 684 |  |  |  |  |  |  | "sup" => { tag=>"mo", output=>"⊃", tex=>"supset", ttype=>"CONST" }, | 
| 685 |  |  |  |  |  |  | "sube" => { tag=>"mo", output=>"⊆", tex=>"subseteq", ttype=>"CONST" }, | 
| 686 |  |  |  |  |  |  | "supe" => { tag=>"mo", output=>"⊇", tex=>"supseteq", ttype=>"CONST" }, | 
| 687 |  |  |  |  |  |  | "-=" => { tag=>"mo", output=>"≡", tex=>"equiv", ttype=>"CONST" }, | 
| 688 |  |  |  |  |  |  | "~=" => { tag=>"mo", output=>"≅", tex=>"cong", ttype=>"CONST" }, | 
| 689 |  |  |  |  |  |  | "~~" => { tag=>"mo", output=>"≈", tex=>"approx", ttype=>"CONST" }, | 
| 690 |  |  |  |  |  |  | "prop" => { tag=>"mo", output=>"∝", tex=>"propto", ttype=>"CONST" }, | 
| 691 |  |  |  |  |  |  |  | 
| 692 |  |  |  |  |  |  | # new for perl | 
| 693 |  |  |  |  |  |  | "<" => { tag=>"mo", output=>"<",      tex=>'', ttype=>"CONST" }, | 
| 694 |  |  |  |  |  |  | "gt" => { tag=>"mo", output=>">",      tex=>'', ttype=>"CONST" }, | 
| 695 |  |  |  |  |  |  | ">" => { tag=>"mo", output=>">",      tex=>'', ttype=>"CONST" }, | 
| 696 |  |  |  |  |  |  | "\\!" => { tag=>"", output=>'',   tex=>'', ttype=>"NOP" }, | 
| 697 |  |  |  |  |  |  |  | 
| 698 |  |  |  |  |  |  |  | 
| 699 |  |  |  |  |  |  | # logical symbols | 
| 700 |  |  |  |  |  |  | "and" => { tag=>"mtext", output=>"and", tex=>'', ttype=>"SPACE" }, | 
| 701 |  |  |  |  |  |  | "or" => { tag=>"mtext", output=>"or",  tex=>'', ttype=>"SPACE" }, | 
| 702 |  |  |  |  |  |  | "not" => { tag=>"mo", output=>"¬", tex=>"neg", ttype=>"CONST" }, | 
| 703 |  |  |  |  |  |  | "=>" => { tag=>"mo", output=>"⇒", tex=>"implies", ttype=>"CONST" }, | 
| 704 |  |  |  |  |  |  | "if" => { tag=>"mo", output=>"if",     tex=>'if', ttype=>"SPACE" }, | 
| 705 |  |  |  |  |  |  | "<=>" => { tag=>"mo", output=>"⇔", tex=>"iff", ttype=>"CONST" }, | 
| 706 |  |  |  |  |  |  | "AA" => { tag=>"mo", output=>"∀", tex=>"forall", ttype=>"CONST" }, | 
| 707 |  |  |  |  |  |  | "EE" => { tag=>"mo", output=>"∃", tex=>"exists", ttype=>"CONST" }, | 
| 708 |  |  |  |  |  |  | "_|_" => { tag=>"mo", output=>"⊥", tex=>"bot", ttype=>"CONST" }, | 
| 709 |  |  |  |  |  |  | "TT" => { tag=>"mo", output=>"⊤", tex=>"top", ttype=>"CONST" }, | 
| 710 |  |  |  |  |  |  | "|--" => { tag=>"mo", output=>"⊢", tex=>"vdash", ttype=>"CONST" }, | 
| 711 |  |  |  |  |  |  | "|==" => { tag=>"mo", output=>"⊨", tex=>"models", ttype=>"CONST" }, | 
| 712 |  |  |  |  |  |  |  | 
| 713 |  |  |  |  |  |  | # grouping brackets | 
| 714 |  |  |  |  |  |  | "(" => { tag=>"mo", output=>"(", tex=>'', ttype=>"LEFTBRACKET" }, | 
| 715 |  |  |  |  |  |  | ")" => { tag=>"mo", output=>")", tex=>'', ttype=>"RIGHTBRACKET" }, | 
| 716 |  |  |  |  |  |  | "[" => { tag=>"mo", output=>"[", tex=>'', ttype=>"LEFTBRACKET" }, | 
| 717 |  |  |  |  |  |  | "]" => { tag=>"mo", output=>"]", tex=>'', ttype=>"RIGHTBRACKET" }, | 
| 718 |  |  |  |  |  |  | "{" => { tag=>"mo", output=>"{", tex=>'', ttype=>"LEFTBRACKET" }, | 
| 719 |  |  |  |  |  |  | "}" => { tag=>"mo", output=>"}", tex=>'', ttype=>"RIGHTBRACKET" }, | 
| 720 |  |  |  |  |  |  | "|" => { tag=>"mo", output=>"|", tex=>'', ttype=>"LEFTRIGHT" }, | 
| 721 |  |  |  |  |  |  | # {input:"||", tag:"mo", output:"||", tex:null, ttype:LEFTRIGHT}, | 
| 722 |  |  |  |  |  |  | "(:" => { tag=>"mo", output=>"〈", tex=>"langle", ttype=>"LEFTBRACKET" }, | 
| 723 |  |  |  |  |  |  | ":)" => { tag=>"mo", output=>"〉", tex=>"rangle", ttype=>"RIGHTBRACKET" }, | 
| 724 |  |  |  |  |  |  | "<<" => { tag=>"mo", output=>"〈", tex=>'langle', ttype=>"LEFTBRACKET" }, | 
| 725 |  |  |  |  |  |  | ">>" => { tag=>"mo", output=>"〉", tex=>'rangle', ttype=>"RIGHTBRACKET" }, | 
| 726 |  |  |  |  |  |  | "{:" => { tag=>"mo", output=>"{:", tex=>'', ttype=>"LEFTBRACKET", invisible=>"true" }, | 
| 727 |  |  |  |  |  |  | ":}" => { tag=>"mo", output=>":}", tex=>'', ttype=>"RIGHTBRACKET", invisible=>"true" }, | 
| 728 |  |  |  |  |  |  |  | 
| 729 |  |  |  |  |  |  | # miscellaneous symbols | 
| 730 |  |  |  |  |  |  | "int" => { tag=>"mo", output=>"∫", tex=>'', ttype=>"CONST" }, | 
| 731 |  |  |  |  |  |  | "dx" => { tag=>"mi", output=>"{:d x:}", tex=>'', ttype=>"DEFINITION" }, | 
| 732 |  |  |  |  |  |  | "dy" => { tag=>"mi", output=>"{:d y:}", tex=>'', ttype=>"DEFINITION" }, | 
| 733 |  |  |  |  |  |  | "dz" => { tag=>"mi", output=>"{:d z:}", tex=>'', ttype=>"DEFINITION" }, | 
| 734 |  |  |  |  |  |  | "dt" => { tag=>"mi", output=>"{:d t:}", tex=>'', ttype=>"DEFINITION" }, | 
| 735 |  |  |  |  |  |  | "oint" => { tag=>"mo", output=>"∮", tex=>'', ttype=>"CONST" }, | 
| 736 |  |  |  |  |  |  | "del" => { tag=>"mo", output=>"∂", tex=>"partial", ttype=>"CONST" }, | 
| 737 |  |  |  |  |  |  | "grad" => { tag=>"mo", output=>"∇", tex=>"nabla", ttype=>"CONST" }, | 
| 738 |  |  |  |  |  |  | "+-" => { tag=>"mo", output=>"±", tex=>"pm", ttype=>"CONST" }, | 
| 739 |  |  |  |  |  |  | "O/" => { tag=>"mo", output=>"∅", tex=>"emptyset", ttype=>"CONST" }, | 
| 740 |  |  |  |  |  |  | "oo" => { tag=>"mo", output=>"∞", tex=>"infty", ttype=>"CONST" }, | 
| 741 |  |  |  |  |  |  | "aleph" => { tag=>"mo", output=>"ℵ", tex=>'', ttype=>"CONST" }, | 
| 742 |  |  |  |  |  |  | "..." => { tag=>"mo", output=>"...",    tex=>"ldots", ttype=>"CONST" }, | 
| 743 |  |  |  |  |  |  | ":." => { tag=>"mo", output=>"∴",  tex=>"therefore", ttype=>"CONST" }, | 
| 744 |  |  |  |  |  |  | "/_" => { tag=>"mo", output=>"∠",  tex=>"angle", ttype=>"CONST" }, | 
| 745 |  |  |  |  |  |  | "&"  => { tag=>"mo", output=>"&",     tex=>'\&',    ttype=>"CONST" }, | 
| 746 |  |  |  |  |  |  | "\\ " => { tag=>"mo", output=>" ", tex=>'\,', ttype=>"CONST" }, | 
| 747 |  |  |  |  |  |  | "quad" => { tag=>"mo", output=>"  ", tex=>'', ttype=>"CONST" }, | 
| 748 |  |  |  |  |  |  | "qquad" => { tag=>"mo", output=>"    ", tex=>'', ttype=>"CONST" }, | 
| 749 |  |  |  |  |  |  | "cdots" => { tag=>"mo", output=>"⋯", tex=>'', ttype=>"CONST" }, | 
| 750 |  |  |  |  |  |  | "vdots" => { tag=>"mo", output=>"⋮", tex=>'', ttype=>"CONST" }, | 
| 751 |  |  |  |  |  |  | "ddots" => { tag=>"mo", output=>"⋱", tex=>'', ttype=>"CONST" }, | 
| 752 |  |  |  |  |  |  | "diamond" => { tag=>"mo", output=>"⋄", tex=>'', ttype=>"CONST" }, | 
| 753 |  |  |  |  |  |  | "square" => { tag=>"mo", output=>"□", tex=>'', ttype=>"CONST" }, | 
| 754 |  |  |  |  |  |  | "|__" => { tag=>"mo", output=>"⌊",  tex=>"lfloor", ttype=>"CONST" }, | 
| 755 |  |  |  |  |  |  | "__|" => { tag=>"mo", output=>"⌋",  tex=>"rfloor", ttype=>"CONST" }, | 
| 756 |  |  |  |  |  |  | "|~" => { tag=>"mo", output=>"⌈",  tex=>"lceil", ttype=>"CONST" }, | 
| 757 |  |  |  |  |  |  | "~|" => { tag=>"mo", output=>"⌉",  tex=>"rceil", ttype=>"CONST" }, | 
| 758 |  |  |  |  |  |  | "CC" => { tag=>"mo", output=>"ℂ", tex=>'', ttype=>"CONST" }, | 
| 759 |  |  |  |  |  |  | "NN" => { tag=>"mo", output=>"ℕ", tex=>'', ttype=>"CONST" }, | 
| 760 |  |  |  |  |  |  | "QQ" => { tag=>"mo", output=>"ℚ", tex=>'', ttype=>"CONST" }, | 
| 761 |  |  |  |  |  |  | "RR" => { tag=>"mo", output=>"ℝ", tex=>'', ttype=>"CONST" }, | 
| 762 |  |  |  |  |  |  | "ZZ" => { tag=>"mo", output=>"ℤ", tex=>'', ttype=>"CONST" }, | 
| 763 |  |  |  |  |  |  | "f" => { tag=>"mi", output=>"f",      tex=>'', ttype=>"UNARY", func=>"true" }, | 
| 764 |  |  |  |  |  |  | "g" => { tag=>"mi", output=>"g",      tex=>'', ttype=>"UNARY", func=>"true" }, | 
| 765 |  |  |  |  |  |  |  | 
| 766 |  |  |  |  |  |  | # standard functions | 
| 767 |  |  |  |  |  |  | "lim" => { tag=>"mo", output=>"lim", tex=>'', ttype=>"UNDEROVER" }, | 
| 768 |  |  |  |  |  |  | "Lim" => { tag=>"mo", output=>"Lim", tex=>'', ttype=>"UNDEROVER" }, | 
| 769 |  |  |  |  |  |  | "sin" => { tag=>"mo", output=>"sin", tex=>'', ttype=>"UNARY", func=>"true" }, | 
| 770 |  |  |  |  |  |  | "cos" => { tag=>"mo", output=>"cos", tex=>'', ttype=>"UNARY", func=>"true" }, | 
| 771 |  |  |  |  |  |  | "tan" => { tag=>"mo", output=>"tan", tex=>'', ttype=>"UNARY", func=>"true" }, | 
| 772 |  |  |  |  |  |  | "sinh" => { tag=>"mo", output=>"sinh", tex=>'', ttype=>"UNARY", func=>"true" }, | 
| 773 |  |  |  |  |  |  | "cosh" => { tag=>"mo", output=>"cosh", tex=>'', ttype=>"UNARY", func=>"true" }, | 
| 774 |  |  |  |  |  |  | "tanh" => { tag=>"mo", output=>"tanh", tex=>'', ttype=>"UNARY", func=>"true" }, | 
| 775 |  |  |  |  |  |  | "cot" => { tag=>"mo", output=>"cot", tex=>'', ttype=>"UNARY", func=>"true" }, | 
| 776 |  |  |  |  |  |  | "sec" => { tag=>"mo", output=>"sec", tex=>'', ttype=>"UNARY", func=>"true" }, | 
| 777 |  |  |  |  |  |  | "csc" => { tag=>"mo", output=>"csc", tex=>'', ttype=>"UNARY", func=>"true" }, | 
| 778 |  |  |  |  |  |  | "log" => { tag=>"mo", output=>"log", tex=>'', ttype=>"UNARY", func=>"true" }, | 
| 779 |  |  |  |  |  |  | "ln" => { tag=>"mo", output=>"ln",  tex=>'', ttype=>"UNARY", func=>"true" }, | 
| 780 |  |  |  |  |  |  | "det" => { tag=>"mo", output=>"det", tex=>'', ttype=>"UNARY", func=>"true" }, | 
| 781 |  |  |  |  |  |  | "dim" => { tag=>"mo", output=>"dim", tex=>'', ttype=>"CONST" }, | 
| 782 |  |  |  |  |  |  | "mod" => { tag=>"mo", output=>"mod", tex=>'', ttype=>"CONST" }, | 
| 783 |  |  |  |  |  |  | "gcd" => { tag=>"mo", output=>"gcd", tex=>'', ttype=>"UNARY", func=>"true" }, | 
| 784 |  |  |  |  |  |  | "lcm" => { tag=>"mo", output=>"lcm", tex=>'', ttype=>"UNARY", func=>"true" }, | 
| 785 |  |  |  |  |  |  | "lub" => { tag=>"mo", output=>"lub", tex=>'', ttype=>"CONST" }, | 
| 786 |  |  |  |  |  |  | "glb" => { tag=>"mo", output=>"glb", tex=>'', ttype=>"CONST" }, | 
| 787 |  |  |  |  |  |  | "min" => { tag=>"mo", output=>"min", tex=>'', ttype=>"UNDEROVER" }, | 
| 788 |  |  |  |  |  |  | "max" => { tag=>"mo", output=>"max", tex=>'', ttype=>"UNDEROVER" }, | 
| 789 |  |  |  |  |  |  |  | 
| 790 |  |  |  |  |  |  | # arrows | 
| 791 |  |  |  |  |  |  | "uarr" => { tag=>"mo", output=>"↑", tex=>"uparrow", ttype=>"CONST" }, | 
| 792 |  |  |  |  |  |  | "darr" => { tag=>"mo", output=>"↓", tex=>"downarrow", ttype=>"CONST" }, | 
| 793 |  |  |  |  |  |  | "rarr" => { tag=>"mo", output=>"→", tex=>"rightarrow", ttype=>"CONST" }, | 
| 794 |  |  |  |  |  |  | "->" => { tag=>"mo", output=>"→", tex=>"to", ttype=>"CONST", latex=>1 }, | 
| 795 |  |  |  |  |  |  | "|->" => { tag=>"mo", output=>"↦", tex=>"mapsto", ttype=>"CONST" }, | 
| 796 |  |  |  |  |  |  | "larr" => { tag=>"mo", output=>"←", tex=>"leftarrow", ttype=>"CONST" }, | 
| 797 |  |  |  |  |  |  | "harr" => { tag=>"mo", output=>"↔", tex=>"leftrightarrow", ttype=>"CONST" }, | 
| 798 |  |  |  |  |  |  | "rArr" => { tag=>"mo", output=>"⇒", tex=>"Rightarrow", ttype=>"CONST", latex=>1 }, | 
| 799 |  |  |  |  |  |  | "lArr" => { tag=>"mo", output=>"⇐", tex=>"Leftarrow", ttype=>"CONST" }, | 
| 800 |  |  |  |  |  |  | "hArr" => { tag=>"mo", output=>"⇔", tex=>"Leftrightarrow", ttype=>"CONST", latex=>1 }, | 
| 801 |  |  |  |  |  |  |  | 
| 802 |  |  |  |  |  |  | # commands with argument | 
| 803 |  |  |  |  |  |  |  | 
| 804 |  |  |  |  |  |  | "hat" => { tag=>"mover", output=>"^", tex=>'', ttype=>"UNARY", acc=>"true" }, | 
| 805 |  |  |  |  |  |  | "bar" => { tag=>"mover", output=>"¯", tex=>"overline", ttype=>"UNARY", acc=>"true" }, | 
| 806 |  |  |  |  |  |  | "vec" => { tag=>"mover", output=>"→", tex=>'', ttype=>"UNARY", acc=>"true" }, | 
| 807 |  |  |  |  |  |  | "dot" => { tag=>"mover", output=>".",      tex=>'', ttype=>"UNARY", acc=>"true" }, | 
| 808 |  |  |  |  |  |  | "ddot" => { tag=>"mover", output=>"..",    tex=>'', ttype=>"UNARY", acc=>"true" }, | 
| 809 |  |  |  |  |  |  | "ul" => { tag=>"munder", output=>"̲", tex=>"underline", ttype=>"UNARY", acc=>"true" }, | 
| 810 |  |  |  |  |  |  |  | 
| 811 |  |  |  |  |  |  | "bb" => { tag=>"mstyle", atname=>"fontweight", atval=>"bold", output=>"bb", tex=>'', ttype=>"UNARY" }, | 
| 812 |  |  |  |  |  |  | "mathbf" => { tag=>"mstyle", atname=>"fontweight", atval=>"bold", output=>"mathbf", tex=>'', ttype=>"UNARY" }, | 
| 813 |  |  |  |  |  |  | "sf" => { tag=>"mstyle", atname=>"fontfamily", atval=>"sans-serif", output=>"sf", tex=>'', ttype=>"UNARY" }, | 
| 814 |  |  |  |  |  |  | "mathsf" => { tag=>"mstyle", atname=>"fontfamily", atval=>"sans-serif", output=>"mathsf", tex=>'', ttype=>"UNARY" }, | 
| 815 |  |  |  |  |  |  | "bbb" => { tag=>"mstyle", atname=>"mathvariant", atval=>"double-struck", output=>"bbb", tex=>'', ttype=>"UNARY", codes=>$AMbbb }, | 
| 816 |  |  |  |  |  |  | "mathbb" => { tag=>"mstyle", atname=>"mathvariant", atval=>"double-struck", output=>"mathbb", tex=>'', ttype=>"UNARY", codes=>$AMbbb }, | 
| 817 |  |  |  |  |  |  | "cc" => { tag=>"mstyle", atname=>"mathvariant", atval=>"script", output=>"cc", tex=>'', ttype=>"UNARY", codes=>$AMcal }, | 
| 818 |  |  |  |  |  |  | "mathcal" => { tag=>"mstyle", atname=>"mathvariant", atval=>"script", output=>"mathcal", tex=>'', ttype=>"UNARY", codes=>$AMcal }, | 
| 819 |  |  |  |  |  |  | "tt" => { tag=>"mstyle", atname=>"fontfamily", atval=>"monospace", output=>"tt", tex=>'', ttype=>"UNARY" }, | 
| 820 |  |  |  |  |  |  | "mathtt" => { tag=>"mstyle", atname=>"fontfamily", atval=>"monospace", output=>"mathtt", tex=>'', ttype=>"UNARY" }, | 
| 821 |  |  |  |  |  |  | "fr" => { tag=>"mstyle", atname=>"mathvariant", atval=>"fraktur", output=>"fr", tex=>'', ttype=>"UNARY", codes=>$AMfrk }, | 
| 822 |  |  |  |  |  |  | "mathfrak" => { tag=>"mstyle", atname=>"mathvariant", atval=>"fraktur", output=>"mathfrak", tex=>'', ttype=>"UNARY", codes=>$AMfrk }, | 
| 823 |  |  |  |  |  |  | ); | 
| 824 |  |  |  |  |  |  |  | 
| 825 |  |  |  |  |  |  | # Preprocess AMSymbol for lexer regular expression | 
| 826 |  |  |  |  |  |  | # Preprocess AMSymbol for tex input | 
| 827 | 1 |  | 33 |  |  | 268 | my %AMTexSym = map(($AMSymbol{$_}{tex} || $_, $_), | 
| 828 |  |  |  |  |  |  | grep($AMSymbol{$_}{tex}, keys %AMSymbol)); | 
| 829 | 1689 |  |  |  |  | 7104 | my $Ident_RE = join '|', map("\Q$_\E", | 
| 830 | 1 |  |  |  |  | 55 | sort {length($b) - length($a)} (keys %AMSymbol, | 
| 831 |  |  |  |  |  |  | keys %AMTexSym)); | 
| 832 |  |  |  |  |  |  |  | 
| 833 |  |  |  |  |  |  | sub _getSymbol_ : method { | 
| 834 | 10482 |  |  | 10482 |  | 20157 | my ($self, $str) = @_; | 
| 835 | 10482 |  |  |  |  | 21594 | for ($str) { | 
| 836 | 10482 | 100 | 100 |  |  | 86743 | /^(\d+(\.\d+)?)/ || /^(\.\d+)/ | 
| 837 |  |  |  |  |  |  | and return $1, {tag=>'mn', output=>$1, ttype=>'CONST'}; | 
| 838 | 9560 | 100 | 100 |  |  | 58463 | $self->{Definition_RE} && /^($self->{Definition_RE})/ and | 
| 839 |  |  |  |  |  |  | return $1, $self->{Definitions}{$1}; | 
| 840 | 9532 | 100 |  |  |  | 57048 | /^($Ident_RE)/o and | 
|  |  | 100 |  |  |  |  |  | 
| 841 |  |  |  |  |  |  | return $1,$AMTexSym{$1} ? $AMSymbol{$AMTexSym{$1}} : $AMSymbol{$1}; | 
| 842 | 5070 | 100 |  |  |  | 31869 | /^([A-Za-z])/ and | 
| 843 |  |  |  |  |  |  | return $1, {tag=>'mi', output=>$1, ttype=>'CONST'}; | 
| 844 | 2924 | 100 | 100 |  |  | 39037 | /^(.)/ and | 
|  |  | 100 |  |  |  |  |  | 
| 845 |  |  |  |  |  |  | return $1 eq '-' && defined $self->{previousSymbol} && | 
| 846 |  |  |  |  |  |  | $self->{previousSymbol} eq 'INFIX' ? | 
| 847 |  |  |  |  |  |  | ($1, {tag=>'mo', output=>$1, ttype=>'UNARY', func=>"true"} ) : | 
| 848 |  |  |  |  |  |  | ($1, {tag=>'mo', output=>$1, ttype=>'CONST'}); | 
| 849 |  |  |  |  |  |  | } | 
| 850 |  |  |  |  |  |  | } | 
| 851 |  |  |  |  |  |  |  | 
| 852 |  |  |  |  |  |  | # Used so that Text::ASCIIMathML::Node can get access to the symbol table | 
| 853 |  |  |  |  |  |  | sub _get_amsymbol_ { | 
| 854 | 1 |  |  | 1 |  | 3 | return \%AMSymbol; | 
| 855 |  |  |  |  |  |  | } | 
| 856 |  |  |  |  |  |  | } | 
| 857 |  |  |  |  |  |  |  | 
| 858 |  |  |  |  |  |  | # Parses an E expression | 
| 859 |  |  |  |  |  |  | # Arguments: string to parse, whether to look for a right bracket | 
| 860 |  |  |  |  |  |  | # Returns: parsed node (if successful), remaining unparsed string | 
| 861 |  |  |  |  |  |  | sub _parseExpr : method { | 
| 862 | 994 |  |  | 994 |  | 1984 | my ($self, $str, $rightbracket) = @_; | 
| 863 | 994 |  |  |  |  | 3000 | my $newFrag = $self->_createDocumentFragment(); | 
| 864 | 994 |  |  |  |  | 9882 | my ($node, $input, $symbol); | 
| 865 | 994 |  | 100 |  |  | 1107 | do { | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 866 | 2335 |  |  |  |  | 5824 | $str = _removeCharsAndBlanks($str, 0); | 
| 867 | 2335 |  |  |  |  | 8684 | ($node, $str) = $self->_parseIexpr($str); | 
| 868 | 2335 |  |  |  |  | 6963 | ($input, $symbol) = $self->_getSymbol($str); | 
| 869 | 2335 | 100 | 100 |  |  | 18720 | if (defined $symbol && $symbol->{ttype} eq 'INFIX' && $input eq '/') { | 
|  |  | 100 | 66 |  |  |  |  | 
| 870 | 96 |  |  |  |  | 222 | $str = _removeCharsAndBlanks($str, length $input); | 
| 871 | 96 |  |  |  |  | 244 | my @result = $self->_parseIexpr($str); | 
| 872 | 96 | 50 |  |  |  | 298 | if ($result[0]) { | 
| 873 | 96 |  |  |  |  | 263 | _removeBrackets($result[0]); | 
| 874 |  |  |  |  |  |  | } | 
| 875 |  |  |  |  |  |  | else { # show box in place of missing argument | 
| 876 | 0 |  |  |  |  | 0 | $result[0] = $self->_createMmlNode | 
| 877 |  |  |  |  |  |  | ('mo', $self->_createTextNode('A1;')); | 
| 878 |  |  |  |  |  |  | } | 
| 879 | 96 |  |  |  |  | 184 | $str = $result[1]; | 
| 880 | 96 |  |  |  |  | 191 | _removeBrackets($node); | 
| 881 | 96 |  |  |  |  | 270 | $node = $self->_createMmlNode($symbol->{tag}, $node); | 
| 882 | 96 |  |  |  |  | 275 | $node->appendChild($result[0]); | 
| 883 | 96 |  |  |  |  | 207 | $newFrag->appendChild($node); | 
| 884 | 96 |  |  |  |  | 213 | ($input, $symbol) = $self->_getSymbol($str); | 
| 885 |  |  |  |  |  |  | } | 
| 886 |  |  |  |  |  |  | elsif (defined $node) { | 
| 887 | 2217 |  |  |  |  | 5615 | $newFrag->appendChild($node); | 
| 888 |  |  |  |  |  |  | } | 
| 889 |  |  |  |  |  |  | } while (defined $symbol && ($symbol->{ttype} ne 'RIGHTBRACKET' && | 
| 890 |  |  |  |  |  |  | ($symbol->{ttype} ne 'LEFTRIGHT'  || | 
| 891 |  |  |  |  |  |  | $rightbracket) | 
| 892 |  |  |  |  |  |  | || $self->{nestingDepth} == 0) && | 
| 893 |  |  |  |  |  |  | $symbol->{output} ne ''); | 
| 894 | 994 | 100 | 66 |  |  | 5283 | if (defined $symbol && $symbol->{ttype} =~ /RIGHTBRACKET|LEFTRIGHT/) { | 
| 895 | 434 |  |  |  |  | 1233 | my @childNodes = $newFrag->childNodes; | 
| 896 | 434 | 100 | 100 |  |  | 2310 | if (@childNodes > 1 && | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 897 |  |  |  |  |  |  | $childNodes[-1]->nodeName eq 'mrow' && | 
| 898 |  |  |  |  |  |  | $childNodes[-2]->nodeName eq 'mo' && | 
| 899 |  |  |  |  |  |  | $childNodes[-2]->firstChild->nodeValue eq ',') { # matrix | 
| 900 | 18 |  |  |  |  | 64 | my $right = $childNodes[-1]->lastChild->firstChild->nodeValue; | 
| 901 | 18 | 50 |  |  |  | 96 | if ($right =~ /[\)\]]/) { | 
| 902 | 18 |  |  |  |  | 61 | my $left = $childNodes[-1]->firstChild->firstChild->nodeValue; | 
| 903 | 18 | 50 | 66 |  |  | 190 | if ("$left$right" =~ /^\(\)$/ && $symbol->{output} ne '}' || | 
|  |  |  | 66 |  |  |  |  | 
| 904 |  |  |  |  |  |  | "$left$right" =~ /^\[\]$/) { | 
| 905 | 18 |  |  |  |  | 30 | my @pos;	# positions of commas | 
| 906 | 18 |  |  |  |  | 29 | my $matrix = 1; | 
| 907 | 18 |  |  |  |  | 29 | my $m = @childNodes; | 
| 908 | 18 |  | 66 |  |  | 103 | for (my $i=0; $matrix && $i < $m; $i += 2) { | 
| 909 | 38 |  |  |  |  | 67 | $pos[$i] = []; | 
| 910 | 38 |  |  |  |  | 55 | $node = $childNodes[$i]; | 
| 911 | 38 | 50 | 33 |  |  | 117 | $matrix = | 
| 912 |  |  |  |  |  |  | $node->nodeName eq 'mrow' && | 
| 913 |  |  |  |  |  |  | ($i == $m-1 || | 
| 914 |  |  |  |  |  |  | $node->nextSibling->nodeName eq 'mo' && | 
| 915 |  |  |  |  |  |  | $node->nextSibling->firstChild->nodeValue eq ',')&& | 
| 916 |  |  |  |  |  |  | $node->firstChild->firstChild->nodeValue eq $left&& | 
| 917 |  |  |  |  |  |  | $node->lastChild->firstChild->nodeValue eq $right | 
| 918 |  |  |  |  |  |  | if $matrix; | 
| 919 | 38 | 50 |  |  |  | 116 | if ($matrix) { | 
| 920 | 38 |  |  |  |  | 117 | for (my $j=0; $j<($node->childNodes); $j++) { | 
| 921 | 210 | 100 |  |  |  | 413 | if (($node->childNodes)[$j]->firstChild-> | 
| 922 |  |  |  |  |  |  | nodeValue eq ',') { | 
| 923 | 40 |  |  |  |  | 46 | push @{$pos[$i]}, $j; | 
|  | 40 |  |  |  |  | 155 |  | 
| 924 |  |  |  |  |  |  | } | 
| 925 |  |  |  |  |  |  | } | 
| 926 |  |  |  |  |  |  | } | 
| 927 | 38 | 100 | 66 |  |  | 225 | if ($matrix && $i > 1) { | 
| 928 | 20 |  |  |  |  | 29 | $matrix = @{$pos[$i]} == @{$pos[$i-2]}; | 
|  | 20 |  |  |  |  | 39 |  | 
|  | 20 |  |  |  |  | 113 |  | 
| 929 |  |  |  |  |  |  | } | 
| 930 |  |  |  |  |  |  | } | 
| 931 | 18 | 50 |  |  |  | 41 | if ($matrix) { | 
| 932 | 18 |  |  |  |  | 44 | my $table = $self->_createDocumentFragment(); | 
| 933 | 18 |  |  |  |  | 72 | for (my $i=0; $i<$m; $i += 2) { | 
| 934 | 38 |  |  |  |  | 130 | my $row  = $self->_createDocumentFragment(); | 
| 935 | 38 |  |  |  |  | 91 | my $frag = $self->_createDocumentFragment(); | 
| 936 |  |  |  |  |  |  | # (-,-,...,-,-) | 
| 937 | 38 |  |  |  |  | 83 | $node = $newFrag->firstChild; | 
| 938 | 38 |  |  |  |  | 87 | my $n = $node->childNodes; | 
| 939 | 38 |  |  |  |  | 53 | my $k = 0; | 
| 940 | 38 |  |  |  |  | 88 | $node->removeChild($node->firstChild); # remove ( | 
| 941 | 38 |  |  |  |  | 127 | for (my $j=1; $j<$n-1; $j++) { | 
| 942 | 134 | 100 | 100 |  |  | 144 | if ($k < @{$pos[$i]} && $j == $pos[$i][$k]) { | 
|  | 134 |  |  |  |  | 542 |  | 
| 943 |  |  |  |  |  |  | # remove , | 
| 944 | 40 |  |  |  |  | 111 | $row->appendChild | 
| 945 |  |  |  |  |  |  | ($self->_createMmlNode('mtd', $frag)); | 
| 946 | 40 |  |  |  |  | 100 | $frag = $self->_createDocumentFragment(); | 
| 947 | 40 |  |  |  |  | 114 | $k++; | 
| 948 |  |  |  |  |  |  | } | 
| 949 |  |  |  |  |  |  | else { | 
| 950 | 94 |  |  |  |  | 204 | $frag->appendChild($node->firstChild); | 
| 951 |  |  |  |  |  |  | } | 
| 952 | 134 |  |  |  |  | 307 | $node->removeChild($node->firstChild); | 
| 953 |  |  |  |  |  |  | } | 
| 954 | 38 |  |  |  |  | 93 | $row->appendChild | 
| 955 |  |  |  |  |  |  | ($self->_createMmlNode('mtd', $frag)); | 
| 956 | 38 | 100 |  |  |  | 86 | if ($newFrag->childNodes > 2) { | 
| 957 |  |  |  |  |  |  | # remove ) | 
| 958 | 20 |  |  |  |  | 53 | $newFrag->removeChild($newFrag->firstChild); | 
| 959 |  |  |  |  |  |  | # remove , | 
| 960 | 20 |  |  |  |  | 65 | $newFrag->removeChild($newFrag->firstChild); | 
| 961 |  |  |  |  |  |  | } | 
| 962 |  |  |  |  |  |  | $table->appendChild | 
| 963 | 38 |  |  |  |  | 115 | ($self->_createMmlNode('mtr', $row)); | 
| 964 |  |  |  |  |  |  | } | 
| 965 | 18 |  |  |  |  | 52 | $node = $self->_createMmlNode('mtable', $table); | 
| 966 | 18 | 100 |  |  |  | 83 | $node->setAttribute('columnalign', 'left') | 
| 967 |  |  |  |  |  |  | if $symbol->{invisible}; | 
| 968 | 18 |  |  |  |  | 60 | $newFrag->replaceChild($node, $newFrag->firstChild); | 
| 969 |  |  |  |  |  |  | } | 
| 970 |  |  |  |  |  |  | } | 
| 971 |  |  |  |  |  |  | } | 
| 972 |  |  |  |  |  |  | } | 
| 973 | 434 |  |  |  |  | 1170 | $str = _removeCharsAndBlanks($str, length $input); | 
| 974 | 434 | 100 |  |  |  | 1743 | if (! $symbol->{invisible}) { | 
| 975 | 390 |  |  |  |  | 1150 | $node = $self->_createMmlNode | 
| 976 |  |  |  |  |  |  | ('mo', $self->_createTextNode($symbol->{output})); | 
| 977 | 390 |  |  |  |  | 934 | $newFrag->appendChild($node); | 
| 978 |  |  |  |  |  |  | } | 
| 979 |  |  |  |  |  |  | } | 
| 980 | 994 |  |  |  |  | 3805 | return $newFrag, $str; | 
| 981 |  |  |  |  |  |  | } | 
| 982 |  |  |  |  |  |  |  | 
| 983 |  |  |  |  |  |  | # Parses an I expression | 
| 984 |  |  |  |  |  |  | # Arguments: string to parse | 
| 985 |  |  |  |  |  |  | # Returns: parsed node (if successful), remaining unparsed string | 
| 986 |  |  |  |  |  |  | sub _parseIexpr : method { | 
| 987 | 2431 |  |  | 2431 |  | 6432 | my ($self, $str) = @_; | 
| 988 | 2431 |  |  |  |  | 4376 | $str = _removeCharsAndBlanks($str, 0); | 
| 989 | 2431 |  |  |  |  | 6322 | my ($in1, $sym1) = $self->_getSymbol($str); | 
| 990 | 2431 |  |  |  |  | 3433 | my $node; | 
| 991 | 2431 |  |  |  |  | 6012 | ($node, $str) = $self->_parseSexpr($str); | 
| 992 | 2431 |  |  |  |  | 7663 | my ($input, $symbol) = $self->_getSymbol($str); | 
| 993 | 2431 | 100 | 100 |  |  | 22163 | if (defined $symbol && $symbol->{ttype} eq 'INFIX' && $input ne '/') { | 
|  |  |  | 100 |  |  |  |  | 
| 994 |  |  |  |  |  |  | #    if (symbol.input == "/") result = AMparseIexpr(str); else ... | 
| 995 | 224 |  |  |  |  | 590 | $str = _removeCharsAndBlanks($str, length $input); | 
| 996 | 224 |  |  |  |  | 621 | my @result = $self->_parseSexpr($str); | 
| 997 | 224 | 50 |  |  |  | 661 | if ($result[0]) { | 
| 998 | 224 |  |  |  |  | 805 | _removeBrackets($result[0]); | 
| 999 |  |  |  |  |  |  | } | 
| 1000 |  |  |  |  |  |  | else { # show box in place of missing argument | 
| 1001 | 0 |  |  |  |  | 0 | $result[0] = $self->_createMmlNode | 
| 1002 |  |  |  |  |  |  | ('mo', $self->_createTextNode("A1;")); | 
| 1003 |  |  |  |  |  |  | } | 
| 1004 | 224 |  |  |  |  | 599 | $str = $result[1]; | 
| 1005 | 224 | 100 |  |  |  | 887 | if ($input eq '_') { | 
|  |  | 50 |  |  |  |  |  | 
| 1006 | 134 |  |  |  |  | 601 | my ($in2, $sym2) = $self->_getSymbol($str); | 
| 1007 | 134 |  |  |  |  | 330 | my $underover = $sym1->{ttype} eq 'UNDEROVER'; | 
| 1008 | 134 | 100 |  |  |  | 442 | if ($in2 eq '^') { | 
| 1009 | 54 |  |  |  |  | 165 | $str = _removeCharsAndBlanks($str, length $in2); | 
| 1010 | 54 |  |  |  |  | 164 | my @res2 = $self->_parseSexpr($str); | 
| 1011 | 54 |  |  |  |  | 373 | _removeBrackets($res2[0]); | 
| 1012 | 54 |  |  |  |  | 118 | $str = $res2[1]; | 
| 1013 | 54 | 100 |  |  |  | 231 | $node = $self->_createMmlNode | 
| 1014 |  |  |  |  |  |  | ($underover ? 'munderover' : 'msubsup', $node); | 
| 1015 | 54 |  |  |  |  | 156 | $node->appendChild($result[0]); | 
| 1016 | 54 |  |  |  |  | 265 | $node->appendChild($res2[0]); | 
| 1017 | 54 |  |  |  |  | 116 | $node = $self->_createMmlNode('mrow',$node); # so sum does not stretch | 
| 1018 |  |  |  |  |  |  | } | 
| 1019 |  |  |  |  |  |  | else { | 
| 1020 | 80 | 100 |  |  |  | 249 | $node = $self->_createMmlNode | 
| 1021 |  |  |  |  |  |  | ($underover ? 'munder' : 'msub', $node); | 
| 1022 | 80 |  |  |  |  | 205 | $node->appendChild($result[0]); | 
| 1023 |  |  |  |  |  |  | } | 
| 1024 |  |  |  |  |  |  | } | 
| 1025 |  |  |  |  |  |  | elsif ($input eq '^') { | 
| 1026 | 90 |  |  |  |  | 214 | my ($in2, $sym2) = $self->_getSymbol($str); | 
| 1027 | 90 |  |  |  |  | 225 | my $underover = $sym1->{ttype} eq 'UNDEROVER'; | 
| 1028 | 90 | 100 |  |  |  | 184 | if ($in2 eq '_') { | 
| 1029 | 2 |  |  |  |  | 8 | $str = _removeCharsAndBlanks($str, length $in2); | 
| 1030 | 2 |  |  |  |  | 7 | my @res2 = $self->_parseSexpr($str); | 
| 1031 | 2 |  |  |  |  | 5 | _removeBrackets($res2[0]); | 
| 1032 | 2 |  |  |  |  | 3 | $str = $res2[1]; | 
| 1033 | 2 | 50 |  |  |  | 14 | $node = $self->_createMmlNode | 
| 1034 |  |  |  |  |  |  | ($underover ? 'munderover' : 'msubsup', $node); | 
| 1035 | 2 |  |  |  |  | 5 | $node->appendChild($res2[0]); | 
| 1036 | 2 |  |  |  |  | 5 | $node->appendChild($result[0]); | 
| 1037 | 2 |  |  |  |  | 6 | $node = $self->_createMmlNode('mrow',$node); # so sum does not stretch | 
| 1038 |  |  |  |  |  |  | } | 
| 1039 |  |  |  |  |  |  | else { | 
| 1040 | 88 | 50 |  |  |  | 250 | $node = $self->_createMmlNode | 
| 1041 |  |  |  |  |  |  | ($underover ? 'mover' : 'msup', $node); | 
| 1042 | 88 |  |  |  |  | 194 | $node->appendChild($result[0]); | 
| 1043 |  |  |  |  |  |  | } | 
| 1044 |  |  |  |  |  |  | } | 
| 1045 |  |  |  |  |  |  | else { | 
| 1046 | 0 |  |  |  |  | 0 | $node = $self->_createMmlNode($symbol->{tag}, $node); | 
| 1047 | 0 |  |  |  |  | 0 | $node->appendChild($result[0]); | 
| 1048 |  |  |  |  |  |  | } | 
| 1049 |  |  |  |  |  |  | } | 
| 1050 | 2431 |  |  |  |  | 10306 | return $node, $str; | 
| 1051 |  |  |  |  |  |  | } | 
| 1052 |  |  |  |  |  |  |  | 
| 1053 |  |  |  |  |  |  | # Parses an S expression | 
| 1054 |  |  |  |  |  |  | # Arguments: string to parse | 
| 1055 |  |  |  |  |  |  | # Returns: parsed node (if successful), remaining unparsed string | 
| 1056 |  |  |  |  |  |  | sub _parseSexpr : method { | 
| 1057 | 2941 |  |  | 2941 |  | 17586 | my ($self, $str) = @_; | 
| 1058 | 2941 |  |  |  |  | 24516 | my $newFrag = $self->_createDocumentFragment(); | 
| 1059 | 2941 |  |  |  |  | 6524 | $str = _removeCharsAndBlanks($str, 0); | 
| 1060 | 2941 |  |  |  |  | 7787 | my ($input, $symbol) = $self->_getSymbol($str); | 
| 1061 | 2941 | 100 | 100 |  |  | 31538 | return (undef, $str) | 
|  |  |  | 66 |  |  |  |  | 
| 1062 |  |  |  |  |  |  | if ! defined $symbol || | 
| 1063 |  |  |  |  |  |  | $symbol->{ttype} eq 'RIGHTBRACKET' && $self->{nestingDepth} > 0; | 
| 1064 | 2897 | 100 |  |  |  | 8364 | if ($symbol->{ttype} eq 'DEFINITION') { | 
| 1065 | 24 |  |  |  |  | 83 | $str = $symbol->{output} . _removeCharsAndBlanks($str, length $input); | 
| 1066 | 24 |  |  |  |  | 62 | ($input, $symbol) = $self->_getSymbol($str); | 
| 1067 |  |  |  |  |  |  | } | 
| 1068 | 2897 |  |  |  |  | 4225 | my $ttype = $symbol->{ttype}; | 
| 1069 | 2897 | 100 |  |  |  | 16652 | if ($ttype =~ /UNDEROVER|CONST/) { | 
| 1070 | 2167 |  |  |  |  | 5046 | $str = _removeCharsAndBlanks($str, length $input); | 
| 1071 |  |  |  |  |  |  | return | 
| 1072 | 2167 |  |  |  |  | 8130 | $self->_createMmlNode($symbol->{tag}, | 
| 1073 |  |  |  |  |  |  | $self->_createTextNode($symbol->{output})), | 
| 1074 |  |  |  |  |  |  | $str; | 
| 1075 |  |  |  |  |  |  | } | 
| 1076 | 730 | 100 |  |  |  | 2379 | if ($ttype eq 'LEFTBRACKET') { | 
| 1077 | 434 |  |  |  |  | 716 | $self->{nestingDepth}++; | 
| 1078 | 434 |  |  |  |  | 922 | $str = _removeCharsAndBlanks($str, length $input); | 
| 1079 | 434 |  |  |  |  | 1791 | my @result = $self->_parseExpr($str, 1); | 
| 1080 | 434 |  |  |  |  | 1348 | $self->{nestingDepth}--; | 
| 1081 | 434 |  |  |  |  | 468 | my $node; | 
| 1082 | 434 | 100 |  |  |  | 856 | if ($symbol->{invisible}) { | 
| 1083 | 44 |  |  |  |  | 161 | $node = $self->_createMmlNode('mrow', $result[0]); | 
| 1084 |  |  |  |  |  |  | } | 
| 1085 |  |  |  |  |  |  | else { | 
| 1086 | 390 |  |  |  |  | 1611 | $node = $self->_createMmlNode | 
| 1087 |  |  |  |  |  |  | ('mo', $self->_createTextNode($symbol->{output})); | 
| 1088 | 390 |  |  |  |  | 963 | $node = $self->_createMmlNode('mrow', $node); | 
| 1089 | 390 |  |  |  |  | 1018 | $node->appendChild($result[0]); | 
| 1090 |  |  |  |  |  |  | } | 
| 1091 | 434 |  |  |  |  | 2954 | return $node, $result[1]; | 
| 1092 |  |  |  |  |  |  | } | 
| 1093 | 296 | 100 |  |  |  | 835 | if ($ttype eq 'TEXT') { | 
| 1094 | 36 | 100 |  |  |  | 86 | $str = _removeCharsAndBlanks($str, length $input) unless $input eq '"'; | 
| 1095 | 36 |  |  |  |  | 43 | my $st; | 
| 1096 | 36 | 100 | 100 |  |  | 378 | ($input, $st) = ($1, $2) | 
| 1097 |  |  |  |  |  |  | if $str =~ /^(\"()\")/ || $str =~ /^(\"((?:\\\\|\\\"|.)+?)\")/; | 
| 1098 | 36 | 100 | 33 |  |  | 311 | ($input, $st) = ($1, $2) | 
|  |  |  | 66 |  |  |  |  | 
| 1099 |  |  |  |  |  |  | if ($str =~ /^(\((.*?)\))/ || | 
| 1100 |  |  |  |  |  |  | $str =~ /^(\[(.*?)\])/ || | 
| 1101 |  |  |  |  |  |  | $str =~ /^(\{(.*?)\})/); | 
| 1102 | 36 | 50 |  |  |  | 66 | ($input, $st) = ($str) x 2 unless defined $st; | 
| 1103 | 36 | 100 |  |  |  | 103 | if (substr($st, 0, 1) eq ' ') { | 
| 1104 | 4 |  |  |  |  | 13 | my $node = $self->_createElementMathML('mspace'); | 
| 1105 | 4 |  |  |  |  | 13 | $node->setAttribute(width=>'1ex'); | 
| 1106 | 4 |  |  |  |  | 8 | $newFrag->appendChild($node); | 
| 1107 |  |  |  |  |  |  | } | 
| 1108 |  |  |  |  |  |  | $newFrag->appendChild | 
| 1109 | 36 |  |  |  |  | 100 | ($self->_createMmlNode($symbol->{tag}, | 
| 1110 |  |  |  |  |  |  | $self->_createTextNode($st))); | 
| 1111 | 36 | 100 |  |  |  | 113 | if (substr($st, -1) eq ' ') { | 
| 1112 | 6 |  |  |  |  | 14 | my $node = $self->_createElementMathML('mspace'); | 
| 1113 | 6 |  |  |  |  | 14 | $node->setAttribute(width=>'1ex'); | 
| 1114 | 6 |  |  |  |  | 11 | $newFrag->appendChild($node); | 
| 1115 |  |  |  |  |  |  | } | 
| 1116 | 36 |  |  |  |  | 119 | $str = _removeCharsAndBlanks($str, length $input); | 
| 1117 | 36 |  |  |  |  | 92 | return $self->_createMmlNode('mrow', $newFrag), $str; | 
| 1118 |  |  |  |  |  |  | } | 
| 1119 | 260 | 100 |  |  |  | 561 | if ($ttype eq 'UNARY') { | 
| 1120 | 180 |  |  |  |  | 427 | $str = _removeCharsAndBlanks($str, length $input); | 
| 1121 | 180 |  |  |  |  | 1297 | my @result = $self->_parseSexpr($str); | 
| 1122 | 180 | 100 |  |  |  | 570 | return ($self->_createMmlNode | 
| 1123 |  |  |  |  |  |  | ($symbol->{tag}, | 
| 1124 |  |  |  |  |  |  | $self->_createTextNode($symbol->{output})), $str) | 
| 1125 |  |  |  |  |  |  | if ! defined $result[0]; | 
| 1126 | 150 | 100 |  |  |  | 388 | if ($symbol->{func}) { | 
| 1127 | 86 | 100 |  |  |  | 377 | return ($self->_createMmlNode | 
| 1128 |  |  |  |  |  |  | ($symbol->{tag}, | 
| 1129 |  |  |  |  |  |  | $self->_createTextNode($symbol->{output})), $str) | 
| 1130 |  |  |  |  |  |  | if $str =~ m!^[\^_/|]!; | 
| 1131 | 72 |  |  |  |  | 254 | my $node = $self->_createMmlNode | 
| 1132 |  |  |  |  |  |  | ('mrow', $self->_createMmlNode | 
| 1133 |  |  |  |  |  |  | ($symbol->{tag}, $self->_createTextNode($symbol->{output}))); | 
| 1134 | 72 |  |  |  |  | 380 | $node->appendChild($result[0]); | 
| 1135 | 72 |  |  |  |  | 365 | return $node, $result[1]; | 
| 1136 |  |  |  |  |  |  | } | 
| 1137 | 64 |  |  |  |  | 200 | _removeBrackets($result[0]); | 
| 1138 | 64 | 100 |  |  |  | 179 | if ($symbol->{acc}) {	# accent | 
| 1139 | 20 |  |  |  |  | 159 | my $node = $self->_createMmlNode($symbol->{tag}, $result[0]); | 
| 1140 | 20 |  |  |  |  | 53 | $node->appendChild | 
| 1141 |  |  |  |  |  |  | ($self->_createMmlNode | 
| 1142 |  |  |  |  |  |  | ('mo', $self->_createTextNode($symbol->{output}))); | 
| 1143 | 20 |  |  |  |  | 118 | return $node, $result[1]; | 
| 1144 |  |  |  |  |  |  | } | 
| 1145 | 44 | 100 |  |  |  | 138 | if ($symbol->{atname}) { # font change command | 
| 1146 | 24 | 100 | 66 |  |  | 175 | if ($self->{attr}{ForMoz} && $symbol->{codes}) { | 
| 1147 | 12 |  |  |  |  | 37 | my @childNodes = $result[0]->childNodes; | 
| 1148 | 12 |  |  |  |  | 32 | my $nodeName = $result[0]->nodeName; | 
| 1149 | 12 |  |  |  |  | 45 | for (my $i=0; $i<@childNodes; $i++) { | 
| 1150 | 18 | 50 | 66 |  |  | 43 | if ($childNodes[$i]->nodeName eq 'mi'||$nodeName eq 'mi') { | 
| 1151 | 18 | 100 |  |  |  | 73 | my $st = $nodeName eq 'mi' ? | 
| 1152 |  |  |  |  |  |  | $result[0]     ->firstChild->nodeValue : | 
| 1153 |  |  |  |  |  |  | $childNodes[$i]->firstChild->nodeValue; | 
| 1154 | 18 |  |  |  |  | 89 | $st =~ s/([A-Z])/sprintf "%X;",$symbol->{codes}[ord($1)-65]/ge; | 
|  | 18 |  |  |  |  | 105 |  | 
| 1155 | 18 | 100 |  |  |  | 157 | if ($nodeName eq 'mi') { | 
| 1156 | 6 |  |  |  |  | 12 | $result[0] = $self->_createTextNode($st); | 
| 1157 |  |  |  |  |  |  | } | 
| 1158 |  |  |  |  |  |  | else { | 
| 1159 | 12 |  |  |  |  | 32 | $result[0]->replaceChild | 
| 1160 |  |  |  |  |  |  | ($self->_createTextNode($st), $childNodes[$i]); | 
| 1161 |  |  |  |  |  |  | } | 
| 1162 |  |  |  |  |  |  | } | 
| 1163 |  |  |  |  |  |  | } | 
| 1164 |  |  |  |  |  |  | } | 
| 1165 | 24 |  |  |  |  | 274 | my $node = $self->_createMmlNode($symbol->{tag}, $result[0]); | 
| 1166 | 24 |  |  |  |  | 160 | $node->setAttribute($symbol->{atname}=>$symbol->{atval}); | 
| 1167 | 24 |  |  |  |  | 149 | return $node, $result[1]; | 
| 1168 |  |  |  |  |  |  | } | 
| 1169 | 20 |  |  |  |  | 74 | return $self->_createMmlNode($symbol->{tag}, $result[0]), $result[1]; | 
| 1170 |  |  |  |  |  |  | } | 
| 1171 | 80 | 100 |  |  |  | 215 | if ($ttype eq 'BINARY') { | 
| 1172 | 24 |  |  |  |  | 66 | $str = _removeCharsAndBlanks($str, length $input); | 
| 1173 | 24 |  |  |  |  | 66 | my @result = $self->_parseSexpr($str); | 
| 1174 | 24 | 50 |  |  |  | 74 | return ($self->_createMmlNode | 
| 1175 |  |  |  |  |  |  | ('mo', $self->_createTextNode($input)), $str) | 
| 1176 |  |  |  |  |  |  | if ! defined $result[0]; | 
| 1177 | 24 |  |  |  |  | 64 | _removeBrackets($result[0]); | 
| 1178 | 24 |  |  |  |  | 91 | my @result2 = $self->_parseSexpr($result[1]); | 
| 1179 | 24 | 50 |  |  |  | 79 | return ($self->_createMmlNode | 
| 1180 |  |  |  |  |  |  | ('mo', $self->_createTextNode($input)), $str) | 
| 1181 |  |  |  |  |  |  | if ! defined $result2[0]; | 
| 1182 | 24 |  |  |  |  | 66 | _removeBrackets($result2[0]); | 
| 1183 | 24 | 100 |  |  |  | 112 | if ($input =~ /new(command|symbol)/) { | 
| 1184 | 10 |  |  |  |  | 28 | my $what = $1; | 
| 1185 |  |  |  |  |  |  | # Look for text in both arguments | 
| 1186 | 10 |  |  |  |  | 18 | my $text1 = $result[0]; | 
| 1187 | 10 |  |  |  |  | 15 | my $haveTextArgs = 0; | 
| 1188 | 10 |  |  |  |  | 26 | $text1 = $text1->firstChild while $text1->nodeName eq 'mrow'; | 
| 1189 | 10 | 50 |  |  |  | 26 | if ($text1->nodeName eq 'mtext') { | 
| 1190 | 10 |  |  |  |  | 12 | my $text2 = $result2[0]; | 
| 1191 | 10 |  |  |  |  | 22 | $text2 = $text2->firstChild while $text2->nodeName eq 'mrow'; | 
| 1192 | 10 |  |  |  |  | 24 | my $latex; | 
| 1193 | 10 | 100 | 66 |  |  | 26 | if ($result2[0]->childNodes > 1 && $input eq 'newsymbol') { | 
| 1194 |  |  |  |  |  |  | # Process the latex string for a newsymbol | 
| 1195 | 4 |  |  |  |  | 14 | my $latexdef = $result2[0]->child(1); | 
| 1196 | 4 |  |  |  |  | 13 | $latexdef = $latexdef->firstChild | 
| 1197 |  |  |  |  |  |  | while $latexdef->nodeName eq 'mrow'; | 
| 1198 | 4 |  |  |  |  | 10 | $latex = $latexdef->firstChild->nodeValue; | 
| 1199 |  |  |  |  |  |  | } | 
| 1200 | 10 | 100 |  |  |  | 54 | if ($text2->nodeName eq 'mtext') { | 
| 1201 | 8 | 100 |  |  |  | 23 | $self->{Definitions}{$text1->firstChild->nodeValue} = { | 
| 1202 |  |  |  |  |  |  | tag   =>'mo', | 
| 1203 |  |  |  |  |  |  | output=>$text2->firstChild->nodeValue, | 
| 1204 |  |  |  |  |  |  | ttype =>$what eq 'symbol' ? 'CONST' : 'DEFINITION', | 
| 1205 |  |  |  |  |  |  | }; | 
| 1206 | 12 |  |  |  |  | 48 | $self->{Definition_RE} = join '|', | 
| 1207 | 8 |  |  |  |  | 56 | map("\Q$_\E", sort {length($b) - length($a)} | 
| 1208 | 8 |  |  |  |  | 24 | keys %{$self->{Definitions}}); | 
| 1209 | 8 | 100 |  |  |  | 36 | $self->{Latex}{$text2->firstChild->nodeValue} = $latex | 
| 1210 |  |  |  |  |  |  | if defined $latex; | 
| 1211 | 8 |  |  |  |  | 20 | $haveTextArgs = 1; | 
| 1212 |  |  |  |  |  |  | } | 
| 1213 |  |  |  |  |  |  | } | 
| 1214 | 10 | 100 |  |  |  | 27 | if (! $haveTextArgs) { | 
| 1215 | 2 |  |  |  |  | 6 | $newFrag->appendChild($self->_createMmlNode | 
| 1216 |  |  |  |  |  |  | ('mo', $self->_createTextNode($input)), | 
| 1217 |  |  |  |  |  |  | $result[0], $result2[0]); | 
| 1218 | 2 |  |  |  |  | 6 | return $self->_createMmlNode('mrow', $newFrag), $result2[1]; | 
| 1219 |  |  |  |  |  |  | } | 
| 1220 | 8 |  |  |  |  | 46 | return undef, $result2[1]; | 
| 1221 |  |  |  |  |  |  | } | 
| 1222 | 14 | 100 |  |  |  | 63 | if ($input =~ /root|stackrel/) { | 
| 1223 | 6 |  |  |  |  | 14 | $newFrag->appendChild($result2[0]); | 
| 1224 |  |  |  |  |  |  | } | 
| 1225 | 14 |  |  |  |  | 44 | $newFrag->appendChild($result[0]); | 
| 1226 | 14 | 100 |  |  |  | 34 | if ($input eq 'frac') { | 
| 1227 | 8 |  |  |  |  | 18 | $newFrag->appendChild($result2[0]); | 
| 1228 |  |  |  |  |  |  | } | 
| 1229 | 14 |  |  |  |  | 42 | return $self->_createMmlNode($symbol->{tag}, $newFrag), $result2[1]; | 
| 1230 |  |  |  |  |  |  | } | 
| 1231 | 56 | 100 |  |  |  | 138 | if ($ttype eq 'INFIX') { | 
| 1232 | 16 |  |  |  |  | 36 | $str = _removeCharsAndBlanks($str, length $input); | 
| 1233 | 16 |  |  |  |  | 50 | return $self->_createMmlNode | 
| 1234 |  |  |  |  |  |  | ('mo', $self->_createTextNode($symbol->{output})), $str; | 
| 1235 |  |  |  |  |  |  | } | 
| 1236 | 40 | 100 |  |  |  | 152 | if ($ttype eq 'SPACE') { | 
| 1237 | 14 |  |  |  |  | 54 | $str = _removeCharsAndBlanks($str, length $input); | 
| 1238 | 14 |  |  |  |  | 39 | my $node = $self->_createElementMathML('mspace'); | 
| 1239 | 14 |  |  |  |  | 41 | $node->setAttribute('width', '1ex'); | 
| 1240 | 14 |  |  |  |  | 36 | $newFrag->appendChild($node); | 
| 1241 | 14 |  |  |  |  | 47 | $newFrag->appendChild | 
| 1242 |  |  |  |  |  |  | ($self->_createMmlNode($symbol->{tag}, | 
| 1243 |  |  |  |  |  |  | $self->_createTextNode($symbol->{output}))); | 
| 1244 | 14 |  |  |  |  | 34 | $node = $self->_createElementMathML('mspace'); | 
| 1245 | 14 |  |  |  |  | 36 | $node->setAttribute('width', '1ex'); | 
| 1246 | 14 |  |  |  |  | 34 | $newFrag->appendChild($node); | 
| 1247 | 14 |  |  |  |  | 35 | return $self->_createMmlNode('mrow', $newFrag), $str; | 
| 1248 |  |  |  |  |  |  | } | 
| 1249 | 26 | 100 |  |  |  | 63 | if ($ttype eq 'LEFTRIGHT') { | 
| 1250 | 12 |  |  |  |  | 20 | $self->{nestingDepth}++; | 
| 1251 | 12 |  |  |  |  | 29 | $str = _removeCharsAndBlanks($str, length $input); | 
| 1252 | 12 |  |  |  |  | 35 | my @result = $self->_parseExpr($str, 0); | 
| 1253 | 12 |  |  |  |  | 34 | $self->{nestingDepth}--; | 
| 1254 | 12 | 50 |  |  |  | 40 | my $st = $result[0]->lastChild ? | 
| 1255 |  |  |  |  |  |  | $result[0]->lastChild->firstChild->nodeValue : ''; | 
| 1256 | 12 |  |  |  |  | 41 | my $node = $self->_createMmlNode | 
| 1257 |  |  |  |  |  |  | ('mo',$self->_createTextNode($symbol->{output})); | 
| 1258 | 12 |  |  |  |  | 39 | $node = $self->_createMmlNode('mrow', $node); | 
| 1259 | 12 | 50 |  |  |  | 40 | if ($st eq '|') { 	# it's an absolute value subterm | 
| 1260 | 12 |  |  |  |  | 27 | $node->appendChild($result[0]); | 
| 1261 | 12 |  |  |  |  | 74 | return $node, $result[1]; | 
| 1262 |  |  |  |  |  |  | } | 
| 1263 |  |  |  |  |  |  | # the "|" is a \mid | 
| 1264 | 0 |  |  |  |  | 0 | return $node, $str; | 
| 1265 |  |  |  |  |  |  | } | 
| 1266 | 14 | 100 |  |  |  | 29 | if ($ttype eq 'NOP') { | 
| 1267 | 2 |  |  |  |  | 7 | $str = _removeCharsAndBlanks($str, length $input); | 
| 1268 | 2 |  |  |  |  | 8 | return $self->_parseSexpr($str); | 
| 1269 |  |  |  |  |  |  | } | 
| 1270 | 12 |  |  |  |  | 28 | $str = _removeCharsAndBlanks($str, length $input); | 
| 1271 | 12 |  |  |  |  | 44 | return $self->_createMmlNode | 
| 1272 |  |  |  |  |  |  | ($symbol->{tag}, # it's a constant | 
| 1273 |  |  |  |  |  |  | $self->_createTextNode($symbol->{output})), $str; | 
| 1274 |  |  |  |  |  |  | } | 
| 1275 |  |  |  |  |  |  |  | 
| 1276 |  |  |  |  |  |  | # Removes brackets at the beginning or end of an mrow node | 
| 1277 |  |  |  |  |  |  | # Arguments: node object | 
| 1278 |  |  |  |  |  |  | # Returns:   None | 
| 1279 |  |  |  |  |  |  | # Side-effects: may change children of node object | 
| 1280 |  |  |  |  |  |  | sub _removeBrackets { | 
| 1281 | 584 |  |  | 584 |  | 881 | my ($node) = @_; | 
| 1282 | 584 | 100 |  |  |  | 1497 | if ($node->nodeName eq 'mrow') { | 
| 1283 | 242 |  |  |  |  | 779 | my $st = $node->firstChild->firstChild->nodeValue; | 
| 1284 | 242 | 100 |  |  |  | 1487 | $node->removeChild($node->firstChild) if $st =~ /^[\(\[\{]$/; | 
| 1285 | 242 |  |  |  |  | 725 | $st = $node->lastChild->firstChild->nodeValue; | 
| 1286 | 242 | 100 |  |  |  | 1142 | $node->removeChild($node->lastChild) if $st =~ /^[\)\]\}]$/; | 
| 1287 |  |  |  |  |  |  | } | 
| 1288 |  |  |  |  |  |  | } | 
| 1289 |  |  |  |  |  |  |  | 
| 1290 |  |  |  |  |  |  | # Removes the first n characters and any following blanks | 
| 1291 |  |  |  |  |  |  | # Arguments: string, n | 
| 1292 |  |  |  |  |  |  | # Returns:   resultant string | 
| 1293 |  |  |  |  |  |  | sub _removeCharsAndBlanks { | 
| 1294 | 11440 |  |  | 11440 |  | 19864 | my ($str, $n) = @_; | 
| 1295 | 11440 | 100 |  |  |  | 36692 | my $st = substr($str, | 
| 1296 |  |  |  |  |  |  | substr($str, $n) =~ /^\\[^\\ ,!]/ ? $n+1 : $n); | 
| 1297 | 11440 |  |  |  |  | 27209 | $st =~ s/^[\x00-\x20]+//; | 
| 1298 | 11440 |  |  |  |  | 27194 | return $st; | 
| 1299 |  |  |  |  |  |  | } | 
| 1300 |  |  |  |  |  |  |  | 
| 1301 |  |  |  |  |  |  | # Removes outermost parenthesis | 
| 1302 |  |  |  |  |  |  | # Arguments: string | 
| 1303 |  |  |  |  |  |  | # Returns:   string with parentheses removed | 
| 1304 |  |  |  |  |  |  | sub _unparen { | 
| 1305 | 0 |  |  | 0 |  | 0 | my ($s) = @_; | 
| 1306 | 0 |  |  |  |  | 0 | $s =~ s!^()[\(\[\{]!$1!; | 
| 1307 | 0 |  |  |  |  | 0 | $s =~ s![\)\]\}]()$!$1!; | 
| 1308 | 0 |  |  |  |  | 0 | $s; | 
| 1309 |  |  |  |  |  |  | } | 
| 1310 |  |  |  |  |  |  |  | 
| 1311 |  |  |  |  |  |  | BEGIN { | 
| 1312 | 1 |  |  | 1 |  | 233 | my %Conversion = ('<'=>'lt', '>'=>'gt', '"'=>'quot', '&'=>'amp'); | 
| 1313 |  |  |  |  |  |  |  | 
| 1314 |  |  |  |  |  |  | # Encodes special xml characters | 
| 1315 |  |  |  |  |  |  | # Arguments: string | 
| 1316 |  |  |  |  |  |  | # Returns:   encoded string | 
| 1317 |  |  |  |  |  |  | sub _xml_encode { | 
| 1318 | 1131 |  |  | 1131 |  | 1512 | my ($s) = @_; | 
| 1319 | 1131 |  |  |  |  | 3238 | $s =~ s/([<>\"&])/&$Conversion{$1};/g; | 
| 1320 | 1131 |  |  |  |  | 5950 | $s; | 
| 1321 |  |  |  |  |  |  | } | 
| 1322 |  |  |  |  |  |  | } | 
| 1323 |  |  |  |  |  |  |  | 
| 1324 |  |  |  |  |  |  | package Text::ASCIIMathML::Node; | 
| 1325 |  |  |  |  |  |  |  | 
| 1326 |  |  |  |  |  |  | { | 
| 1327 |  |  |  |  |  |  | # Create a closure for the following attributes | 
| 1328 |  |  |  |  |  |  | my %parser_of; | 
| 1329 |  |  |  |  |  |  |  | 
| 1330 |  |  |  |  |  |  | # Creates a new Text::ASCIIMathML::Node object | 
| 1331 |  |  |  |  |  |  | # Arguments: Text::ASCIIMathML object, optional tag | 
| 1332 |  |  |  |  |  |  | # Returns: new object | 
| 1333 |  |  |  |  |  |  | sub new { | 
| 1334 | 9536 |  |  | 9536 |  | 22965 | my ($class, $parser, $tag) = @_; | 
| 1335 | 9536 |  |  |  |  | 42409 | my $obj = bless { children=>[] }, $class; | 
| 1336 | 9536 | 100 |  |  |  | 29418 | if (defined $tag) { $obj->{tag} = $tag } | 
|  | 5466 |  |  |  |  | 18195 |  | 
| 1337 | 4070 |  |  |  |  | 10920 | else { $obj->{frag} = 1 } | 
| 1338 | 9536 |  |  |  |  | 39639 | $parser_of{$obj} = $parser; | 
| 1339 | 9536 |  |  |  |  | 31164 | return $obj; | 
| 1340 |  |  |  |  |  |  | } | 
| 1341 |  |  |  |  |  |  |  | 
| 1342 |  |  |  |  |  |  | # Creates a new Text::ASCIIMathML::Node text object | 
| 1343 |  |  |  |  |  |  | # Arguments: Text::ASCIIMathML object, text | 
| 1344 |  |  |  |  |  |  | # Returns: new object | 
| 1345 |  |  |  |  |  |  | sub newText { | 
| 1346 | 3193 |  |  | 3193 |  | 5115 | my ($class, $parser, $text) = @_; | 
| 1347 | 3193 |  |  |  |  | 20999 | $text =~ s/^\s*(.*?)\s*$/$1/;	# Delete leading/trailing spaces | 
| 1348 | 3193 |  |  |  |  | 14451 | my $obj = bless { text=>$text }, $class; | 
| 1349 | 3193 |  |  |  |  | 14132 | $parser_of{$obj} = $parser; | 
| 1350 | 3193 |  |  |  |  | 10816 | return $obj; | 
| 1351 |  |  |  |  |  |  | } | 
| 1352 |  |  |  |  |  |  |  | 
| 1353 |  |  |  |  |  |  | my %Parent; | 
| 1354 |  |  |  |  |  |  | my $Null; | 
| 1355 |  |  |  |  |  |  | BEGIN { | 
| 1356 | 1 |  |  | 1 |  | 8 | $Null = new Text::ASCIIMathML::Node; | 
| 1357 |  |  |  |  |  |  | } | 
| 1358 |  |  |  |  |  |  |  | 
| 1359 |  |  |  |  |  |  | # Appends one or more node objects to the children of an object | 
| 1360 |  |  |  |  |  |  | # Arguments: list of objects to append | 
| 1361 |  |  |  |  |  |  | # Returns:   self | 
| 1362 |  |  |  |  |  |  | sub appendChild : method { | 
| 1363 | 9329 |  |  | 9329 |  | 24289 | my $self = shift; | 
| 1364 | 9329 | 100 |  |  |  | 45126 | my @new = map $_->{frag} ? @{$_->{children}} : $_, @_; | 
|  | 1194 |  |  |  |  | 3888 |  | 
| 1365 | 9329 |  |  |  |  | 11451 | push @{$self->{children}}, @new; | 
|  | 9329 |  |  |  |  | 21513 |  | 
| 1366 | 9329 |  |  |  |  | 14245 | map do {$Parent{$_} = $self}, @new; | 
|  | 11134 |  |  |  |  | 62885 |  | 
| 1367 | 9329 |  |  |  |  | 33888 | return $self; | 
| 1368 |  |  |  |  |  |  | } | 
| 1369 |  |  |  |  |  |  |  | 
| 1370 |  |  |  |  |  |  | # Returns a the value for an attribute of a node object | 
| 1371 |  |  |  |  |  |  | # Arguments: Attribute name | 
| 1372 |  |  |  |  |  |  | # Returns:   Value for the attribute | 
| 1373 |  |  |  |  |  |  | sub attribute { | 
| 1374 | 0 |  |  | 0 |  | 0 | my ($self, $attr) = @_; | 
| 1375 | 0 |  |  |  |  | 0 | return $self->{attr}{$attr}; | 
| 1376 |  |  |  |  |  |  | } | 
| 1377 |  |  |  |  |  |  |  | 
| 1378 |  |  |  |  |  |  | # Returns a list of the attributes of a node object | 
| 1379 |  |  |  |  |  |  | # Arguments: None | 
| 1380 |  |  |  |  |  |  | # Returns:   Array of attribute names | 
| 1381 |  |  |  |  |  |  | sub attributeList { | 
| 1382 | 0 |  |  | 0 |  | 0 | my ($self) = @_; | 
| 1383 | 0 | 0 |  |  |  | 0 | return $self->{attrlist} ? @{$self->{attrlist}} : (); | 
|  | 0 |  |  |  |  | 0 |  | 
| 1384 |  |  |  |  |  |  | } | 
| 1385 |  |  |  |  |  |  |  | 
| 1386 |  |  |  |  |  |  | # Returns a child with a given index in the array of children of a node | 
| 1387 |  |  |  |  |  |  | # Arguments: index | 
| 1388 |  |  |  |  |  |  | # Returns:   Array of node objects | 
| 1389 |  |  |  |  |  |  | sub child { | 
| 1390 | 7 |  |  | 7 |  | 18 | my ($self, $index) = @_; | 
| 1391 | 7 | 100 | 66 |  |  | 28 | return $self->{children} && @{$self->{children}} > $index ? | 
| 1392 |  |  |  |  |  |  | $self->{children}[$index] : $Null; | 
| 1393 |  |  |  |  |  |  | } | 
| 1394 |  |  |  |  |  |  |  | 
| 1395 |  |  |  |  |  |  | # Returns an array of children of a node | 
| 1396 |  |  |  |  |  |  | # Arguments: None | 
| 1397 |  |  |  |  |  |  | # Returns:   Array of node objects | 
| 1398 |  |  |  |  |  |  | sub childNodes { | 
| 1399 | 1538 |  |  | 1538 |  | 6297 | my ($self) = @_; | 
| 1400 | 1538 | 50 |  |  |  | 3495 | return $self->{children} ? @{$self->{children}} : (); | 
|  | 1538 |  |  |  |  | 5238 |  | 
| 1401 |  |  |  |  |  |  | } | 
| 1402 |  |  |  |  |  |  |  | 
| 1403 |  |  |  |  |  |  | # Returns the first child of a node; ignores any fragments | 
| 1404 |  |  |  |  |  |  | # Arguments: None | 
| 1405 |  |  |  |  |  |  | # Returns:   node object or self | 
| 1406 |  |  |  |  |  |  | sub firstChild { | 
| 1407 | 1812 |  |  | 1812 |  | 2392 | my ($self) = @_; | 
| 1408 | 1812 | 50 | 33 |  |  | 12316 | return $self->{children} && @{$self->{children}} ? | 
| 1409 |  |  |  |  |  |  | $self->{children}[0] : $Null; | 
| 1410 |  |  |  |  |  |  | } | 
| 1411 |  |  |  |  |  |  |  | 
| 1412 |  |  |  |  |  |  | # Returns true if the object is a fragment | 
| 1413 |  |  |  |  |  |  | # Arguments: None | 
| 1414 |  |  |  |  |  |  | # Returns:   Boolean | 
| 1415 |  |  |  |  |  |  | sub isFragment { | 
| 1416 | 0 |  |  | 0 |  | 0 | return $_[0]->{frag}; | 
| 1417 |  |  |  |  |  |  | } | 
| 1418 |  |  |  |  |  |  |  | 
| 1419 |  |  |  |  |  |  | # Returns true if the object is a named node | 
| 1420 |  |  |  |  |  |  | # Arguments: None | 
| 1421 |  |  |  |  |  |  | # Returns:   Boolean | 
| 1422 |  |  |  |  |  |  | sub isNamed { | 
| 1423 | 0 |  |  | 0 |  | 0 | return $_[0]->{tag}; | 
| 1424 |  |  |  |  |  |  | } | 
| 1425 |  |  |  |  |  |  |  | 
| 1426 |  |  |  |  |  |  | # Returns true if the object is a text node | 
| 1427 |  |  |  |  |  |  | # Arguments: None | 
| 1428 |  |  |  |  |  |  | # Returns:   Boolean | 
| 1429 |  |  |  |  |  |  | sub isText { | 
| 1430 | 0 |  |  | 0 |  | 0 | return defined $_[0]->{text}; | 
| 1431 |  |  |  |  |  |  | } | 
| 1432 |  |  |  |  |  |  |  | 
| 1433 |  |  |  |  |  |  | # Returns the last child of a node | 
| 1434 |  |  |  |  |  |  | # Arguments: None | 
| 1435 |  |  |  |  |  |  | # Returns:   node object or self | 
| 1436 |  |  |  |  |  |  | sub lastChild { | 
| 1437 | 528 |  |  | 528 |  | 824 | my ($self) = @_; | 
| 1438 | 528 | 50 | 33 |  |  | 1373 | return  $self->{children} && @{$self->{children}} ? | 
| 1439 |  |  |  |  |  |  | $self->{children}[-1] : $Null; | 
| 1440 |  |  |  |  |  |  | } | 
| 1441 |  |  |  |  |  |  |  | 
| 1442 |  |  |  |  |  |  | BEGIN { | 
| 1443 |  |  |  |  |  |  | # Creates closure for following "static" variables | 
| 1444 | 1 |  |  | 1 |  | 966 | my (%LatexSym, %LatexMover, %LatexFont, %LatexOp); | 
| 1445 |  |  |  |  |  |  |  | 
| 1446 |  |  |  |  |  |  | # Returns a latex representation of a node object | 
| 1447 |  |  |  |  |  |  | # Arguments: None | 
| 1448 |  |  |  |  |  |  | # Returns:   Text string | 
| 1449 |  |  |  |  |  |  | sub latex : method { | 
| 1450 | 3689 |  |  | 3689 |  | 6898 | my ($self) = @_; | 
| 1451 |  |  |  |  |  |  |  | 
| 1452 | 3689 |  |  |  |  | 11881 | my $parser = $parser_of{$self}; | 
| 1453 | 3689 | 100 |  |  |  | 14189 | if (! %LatexSym) { | 
| 1454 |  |  |  |  |  |  | # Build the entity to latex symbol translator | 
| 1455 | 1 |  |  |  |  | 4 | my $amsymbol = Text::ASCIIMathML::_get_amsymbol_(); | 
| 1456 | 1 |  |  |  |  | 58 | foreach my $sym (keys %$amsymbol) { | 
| 1457 | 203 | 100 | 100 |  |  | 1200 | next unless (defined $amsymbol->{$sym}{output} && | 
| 1458 |  |  |  |  |  |  | $amsymbol->{$sym}{output} =~ /&\#x.*;/); | 
| 1459 | 125 |  |  |  |  | 424 | my ($output, $tex) = map $amsymbol->{$sym}{$_}, qw(output tex); | 
| 1460 | 125 | 100 | 100 |  |  | 342 | next if defined $LatexSym{$output} && ! $amsymbol->{$sym}{latex}; | 
| 1461 | 118 | 100 |  |  |  | 217 | $tex = $sym if $tex eq ''; | 
| 1462 | 118 |  |  |  |  | 359 | $LatexSym{$output} = "\\$tex"; | 
| 1463 |  |  |  |  |  |  | } | 
| 1464 | 1 |  |  |  |  | 22 | my %math_font = (bbb      => 'mathds', | 
| 1465 |  |  |  |  |  |  | mathbb   => 'mathds', | 
| 1466 |  |  |  |  |  |  | cc       => 'cal', | 
| 1467 |  |  |  |  |  |  | mathcal  => 'cal', | 
| 1468 |  |  |  |  |  |  | fr       => 'mathfrak', | 
| 1469 |  |  |  |  |  |  | mathfrak => 'mathfrak', | 
| 1470 |  |  |  |  |  |  | ); | 
| 1471 |  |  |  |  |  |  | # Add character codes | 
| 1472 | 1 |  |  |  |  | 89 | foreach my $coded (grep $amsymbol->{$_}{codes}, keys %$amsymbol) { | 
| 1473 | 6 |  |  |  |  | 294 | @LatexSym{map(sprintf("%X;", $_), | 
| 1474 | 6 |  |  |  |  | 204 | @{$amsymbol->{$coded}{codes}})} = | 
| 1475 |  |  |  |  |  |  | map("\\$math_font{$coded}\{$_}", ('A' .. 'Z')); | 
| 1476 |  |  |  |  |  |  | } | 
| 1477 |  |  |  |  |  |  | # Post-process protected symbols | 
| 1478 | 1 |  |  |  |  | 116 | $LatexSym{$_} =~ s/^\\\\/\\/ foreach keys %LatexSym; | 
| 1479 | 1 |  |  |  |  | 20 | %LatexMover = ('^'           => '\hat', | 
| 1480 |  |  |  |  |  |  | '\overline'   => '\overline', | 
| 1481 |  |  |  |  |  |  | '\to'         => '\vec', | 
| 1482 |  |  |  |  |  |  | '\vec'        => '\vec', | 
| 1483 |  |  |  |  |  |  | '\rightarrow' => '\vec', | 
| 1484 |  |  |  |  |  |  | '.'           => '\dot', | 
| 1485 |  |  |  |  |  |  | '..'          => '\ddot', | 
| 1486 |  |  |  |  |  |  | ); | 
| 1487 | 1 |  |  |  |  | 8 | %LatexFont = (bold            => '\bf', | 
| 1488 |  |  |  |  |  |  | 'double-struck' => '\mathds', | 
| 1489 |  |  |  |  |  |  | fraktur         => '\mathfrak', | 
| 1490 |  |  |  |  |  |  | monospace       => '\tt', | 
| 1491 |  |  |  |  |  |  | 'sans-serif'    => '\sf', | 
| 1492 |  |  |  |  |  |  | script          => '\cal', | 
| 1493 |  |  |  |  |  |  | ); | 
| 1494 | 1 |  |  |  |  | 12 | %LatexOp = (if         => '\mbox{if }', | 
| 1495 |  |  |  |  |  |  | lcm        => '\mbox{lcm}', | 
| 1496 |  |  |  |  |  |  | newcommand => '\mbox{newcommand}', | 
| 1497 |  |  |  |  |  |  | "\\"       => '\backslash', | 
| 1498 |  |  |  |  |  |  | '<'     => '<', | 
| 1499 |  |  |  |  |  |  | '>'     => '>', | 
| 1500 |  |  |  |  |  |  | '&'    => '\&', | 
| 1501 |  |  |  |  |  |  | '...'      => '\ldots', | 
| 1502 |  |  |  |  |  |  | ); | 
| 1503 |  |  |  |  |  |  | } | 
| 1504 | 3689 | 100 |  |  |  | 22347 | if (defined $self->{text}) { | 
| 1505 | 1294 |  |  |  |  | 2719 | my $text = $self->{text}; | 
| 1506 | 1294 |  |  |  |  | 2990 | $text =~ s/([{}])/\\$1/; | 
| 1507 | 1294 |  |  |  |  | 5353 | $text =~ s/(&\#x.*?;)/ | 
| 1508 | 260 | 50 |  |  |  | 2934 | defined $parser->{Latex}{$1} ? $parser->{Latex}{$1} : | 
|  |  | 100 |  |  |  |  |  | 
| 1509 |  |  |  |  |  |  | defined $LatexSym{$1} ? $LatexSym{$1} : $1/eg; | 
| 1510 | 1294 |  |  |  |  | 1804 | $text =~ s/([\#])/\\$1/; | 
| 1511 | 1294 |  |  |  |  | 5252 | return $text; | 
| 1512 |  |  |  |  |  |  | } | 
| 1513 | 2395 |  |  |  |  | 4571 | my $tag = $self->{tag}; | 
| 1514 | 2395 |  |  |  |  | 2924 | my @child_str; | 
| 1515 | 2395 |  |  |  |  | 3084 | my $child_str = ''; | 
| 1516 | 2395 | 100 |  |  |  | 2787 | if (@{$self->{children}}) { | 
|  | 2395 |  |  |  |  | 7672 |  | 
| 1517 | 2373 |  |  |  |  | 3314 | foreach (@{$self->{children}}) { | 
|  | 2373 |  |  |  |  | 5222 |  | 
| 1518 | 3416 |  |  |  |  | 14150 | push @child_str, $_->latex($parser); | 
| 1519 |  |  |  |  |  |  | } | 
| 1520 |  |  |  |  |  |  | } | 
| 1521 |  |  |  |  |  |  |  | 
| 1522 |  |  |  |  |  |  | # mo | 
| 1523 | 2395 | 100 |  |  |  | 12155 | if ($tag eq 'mo') { | 
| 1524 |  |  |  |  |  |  | # Need to distinguish bmod from pmod | 
| 1525 | 623 |  |  |  |  | 2028 | my $parent = $self->parent; | 
| 1526 | 623 | 100 | 66 |  |  | 2453 | return $self eq $parent->child(1) && | 
|  |  | 100 |  |  |  |  |  | 
| 1527 |  |  |  |  |  |  | $parent->firstChild->firstChild->{text} eq '(' | 
| 1528 |  |  |  |  |  |  | ? '\pmod' : '\bmod' | 
| 1529 |  |  |  |  |  |  | if $child_str[0] eq 'mod'; | 
| 1530 | 620 | 100 |  |  |  | 1532 | return $LatexOp{$child_str[0]} if $LatexOp{$child_str[0]}; | 
| 1531 | 597 | 100 |  |  |  | 6017 | return $child_str[0] =~ /^\w+$/ ? "\\$child_str[0]" : $child_str[0]; | 
| 1532 |  |  |  |  |  |  | } | 
| 1533 |  |  |  |  |  |  |  | 
| 1534 |  |  |  |  |  |  | # mrow | 
| 1535 | 1772 | 100 |  |  |  | 3557 | if ($tag eq 'mrow') { | 
| 1536 | 279 |  |  |  |  | 1265 | @child_str = grep $_ ne '', @child_str; | 
| 1537 |  |  |  |  |  |  | # Check for pmod function | 
| 1538 | 279 | 100 | 100 |  |  | 1322 | if (@child_str > 1 && $child_str[1] eq '\pmod') { | 
| 1539 | 1 | 50 |  |  |  | 6 | pop @child_str if $child_str[-1] eq ')'; | 
| 1540 | 1 |  |  |  |  | 4 | splice @child_str, 0, 2; | 
| 1541 | 1 |  |  |  |  | 111 | return "\\pmod{@child_str}"; | 
| 1542 |  |  |  |  |  |  | } | 
| 1543 |  |  |  |  |  |  | # Check if we need \left ... \right | 
| 1544 | 278 |  |  |  |  | 1988 | my $is_tall = grep(/[_^]|\\(begin\{array\}|frac|sqrt|stackrel)/, | 
| 1545 |  |  |  |  |  |  | @child_str); | 
| 1546 | 278 | 100 | 100 |  |  | 1243 | if ($is_tall && @child_str > 1 && | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 1547 |  |  |  |  |  |  | ($child_str[0]  =~ /^([\(\[|]|\\\{)$/ || | 
| 1548 |  |  |  |  |  |  | $child_str[-1] =~ /^([\)\]|]|\\\})$/)) { | 
| 1549 | 27 | 50 |  |  |  | 134 | if ($child_str[0] =~ /^([\(\[|]|\\\{)$/) { | 
| 1550 | 27 |  |  |  |  | 69 | $child_str[0] = "\\left$child_str[0]"; | 
| 1551 |  |  |  |  |  |  | } | 
| 1552 |  |  |  |  |  |  | else { | 
| 1553 | 0 |  |  |  |  | 0 | unshift @child_str, "\\left."; | 
| 1554 |  |  |  |  |  |  | } | 
| 1555 | 27 | 100 |  |  |  | 206 | if ($child_str[-1] =~ /^([\)\]|]|\\\})$/) { | 
| 1556 | 26 |  |  |  |  | 67 | $child_str[-1] = "\\right$child_str[-1]"; | 
| 1557 |  |  |  |  |  |  | } | 
| 1558 |  |  |  |  |  |  | else { | 
| 1559 | 1 |  |  |  |  | 4 | push @child_str, "\\right."; | 
| 1560 |  |  |  |  |  |  | } | 
| 1561 |  |  |  |  |  |  | } | 
| 1562 | 278 |  |  |  |  | 1759 | return "@child_str"; | 
| 1563 |  |  |  |  |  |  | } | 
| 1564 |  |  |  |  |  |  |  | 
| 1565 |  |  |  |  |  |  |  | 
| 1566 |  |  |  |  |  |  | # mi | 
| 1567 |  |  |  |  |  |  | # mn | 
| 1568 |  |  |  |  |  |  | # math | 
| 1569 |  |  |  |  |  |  | # mtd | 
| 1570 | 1493 | 100 |  |  |  | 10634 | if ($tag =~ /^m([in]|ath|row|td)$/) { | 
| 1571 | 963 |  |  |  |  | 3610 | @child_str = grep $_ ne '', @child_str; | 
| 1572 | 963 |  |  |  |  | 5220 | return "@child_str"; | 
| 1573 |  |  |  |  |  |  | } | 
| 1574 |  |  |  |  |  |  |  | 
| 1575 |  |  |  |  |  |  | # msub | 
| 1576 |  |  |  |  |  |  | # msup | 
| 1577 |  |  |  |  |  |  | # msubsup | 
| 1578 |  |  |  |  |  |  | # munderover | 
| 1579 | 530 | 100 |  |  |  | 1976 | if ($tag =~ /^(msu[bp](sup)?|munderover)$/) { | 
| 1580 | 108 |  |  |  |  | 235 | my $base = shift @child_str; | 
| 1581 | 108 | 100 |  |  |  | 248 | $base = '\mbox{}' if $base eq ''; | 
| 1582 |  |  |  |  |  |  | # Put {} around arguments with more than one character | 
| 1583 | 108 | 100 |  |  |  | 882 | @child_str = map length($_) > 1 ? "{$_}" : $_, @child_str; | 
| 1584 | 108 | 100 |  |  |  | 880 | return ($tag eq 'msub' ? "${base}_$child_str[0]" : | 
|  |  | 100 |  |  |  |  |  | 
| 1585 |  |  |  |  |  |  | $tag eq 'msup' ? "${base}^$child_str[0]" : | 
| 1586 |  |  |  |  |  |  | "${base}_$child_str[0]^$child_str[1]"); | 
| 1587 |  |  |  |  |  |  | } | 
| 1588 |  |  |  |  |  |  |  | 
| 1589 |  |  |  |  |  |  | # mover | 
| 1590 | 422 | 100 |  |  |  | 906 | if ($tag eq 'mover') { | 
| 1591 |  |  |  |  |  |  | # Need to special-case math mode accents | 
| 1592 |  |  |  |  |  |  | return | 
| 1593 | 11 | 100 | 66 |  |  | 100 | ($child_str[1] eq '\overline' && length($child_str[0]) == 1 ? | 
|  |  | 100 |  |  |  |  |  | 
| 1594 |  |  |  |  |  |  | "\\bar{$child_str[0]}" : | 
| 1595 |  |  |  |  |  |  | $LatexMover{$child_str[1]} ? | 
| 1596 |  |  |  |  |  |  | "$LatexMover{$child_str[1]}\{$child_str[0]\}" : | 
| 1597 |  |  |  |  |  |  | "\\stackrel{$child_str[1]}{$child_str[0]}"); | 
| 1598 |  |  |  |  |  |  | } | 
| 1599 |  |  |  |  |  |  |  | 
| 1600 |  |  |  |  |  |  | # munder | 
| 1601 | 411 | 100 |  |  |  | 1359 | if ($tag eq 'munder') { | 
| 1602 | 5 | 100 |  |  |  | 36 | return $child_str[1] eq '\underline' ? "$child_str[1]\{$child_str[0]}" | 
| 1603 |  |  |  |  |  |  | : "$child_str[0]_\{$child_str[1]\}"; | 
| 1604 |  |  |  |  |  |  | } | 
| 1605 |  |  |  |  |  |  |  | 
| 1606 |  |  |  |  |  |  | # mfrac | 
| 1607 | 406 | 100 |  |  |  | 992 | if ($tag eq 'mfrac') { | 
| 1608 | 52 |  |  |  |  | 285 | return "\\frac{$child_str[0]}{$child_str[1]}"; | 
| 1609 |  |  |  |  |  |  | } | 
| 1610 |  |  |  |  |  |  |  | 
| 1611 |  |  |  |  |  |  | # msqrt | 
| 1612 | 354 | 100 |  |  |  | 863 | if ($tag eq 'msqrt') { | 
| 1613 | 10 |  |  |  |  | 76 | return "\\sqrt{$child_str[0]}"; | 
| 1614 |  |  |  |  |  |  | } | 
| 1615 |  |  |  |  |  |  |  | 
| 1616 |  |  |  |  |  |  | # mroot | 
| 1617 | 344 | 100 |  |  |  | 719 | if ($tag eq 'mroot') { | 
| 1618 | 1 |  |  |  |  | 7 | return "\\sqrt[$child_str[1]]{$child_str[0]}"; | 
| 1619 |  |  |  |  |  |  | } | 
| 1620 |  |  |  |  |  |  |  | 
| 1621 |  |  |  |  |  |  | # mtext | 
| 1622 | 343 | 100 |  |  |  | 1047 | if ($tag eq 'mtext') { | 
| 1623 | 11 |  |  |  |  | 24 | my $text = $child_str[0]; | 
| 1624 | 11 |  |  |  |  | 33 | my $next = $self->nextSibling; | 
| 1625 | 11 |  |  |  |  | 40 | my $prev = $self->previousSibling; | 
| 1626 | 11 | 100 | 66 |  |  | 56 | if (defined $next->{tag} && $next->{tag} eq 'mspace') { | 
| 1627 | 6 |  |  |  |  | 20 | $text = "$text "; | 
| 1628 |  |  |  |  |  |  | } | 
| 1629 | 11 | 100 | 66 |  |  | 48 | if (defined $prev->{tag} && $prev->{tag} eq 'mspace') { | 
| 1630 | 5 |  |  |  |  | 11 | $text = " $text"; | 
| 1631 |  |  |  |  |  |  | } | 
| 1632 | 11 | 100 |  |  |  | 330 | $text = ' ' if $text eq '  '; | 
| 1633 | 11 |  |  |  |  | 56 | return "\\mbox{$text}"; | 
| 1634 |  |  |  |  |  |  | } | 
| 1635 |  |  |  |  |  |  |  | 
| 1636 |  |  |  |  |  |  |  | 
| 1637 |  |  |  |  |  |  | # mspace | 
| 1638 | 332 | 100 |  |  |  | 657 | if ($tag eq 'mspace') { | 
| 1639 | 19 |  |  |  |  | 77 | return ''; | 
| 1640 |  |  |  |  |  |  | } | 
| 1641 |  |  |  |  |  |  |  | 
| 1642 |  |  |  |  |  |  | # mtable | 
| 1643 | 313 | 100 |  |  |  | 652 | if ($tag eq 'mtable') { | 
| 1644 | 9 |  |  |  |  | 31 | my $cols = ($child_str[0] =~ tr/&//) + 1; | 
| 1645 | 9 | 100 | 100 |  |  | 70 | my $colspec = ($self->{attr}{columnalign} || '') eq 'left' ? 'l' : 'c'; | 
| 1646 | 9 |  |  |  |  | 36 | my $colspecs = $colspec x $cols; | 
| 1647 | 9 |  |  |  |  | 91 | return ("\\begin{array}{$colspecs}\n" . | 
| 1648 |  |  |  |  |  |  | join('', map("  $_ \\\\\n", @child_str)) . | 
| 1649 |  |  |  |  |  |  | "\\end{array}\n"); | 
| 1650 |  |  |  |  |  |  | } | 
| 1651 |  |  |  |  |  |  |  | 
| 1652 |  |  |  |  |  |  | # mtr | 
| 1653 | 304 | 100 |  |  |  | 672 | if ($tag eq 'mtr') { | 
| 1654 | 19 |  |  |  |  | 89 | return join ' & ', @child_str; | 
| 1655 |  |  |  |  |  |  | } | 
| 1656 |  |  |  |  |  |  |  | 
| 1657 |  |  |  |  |  |  | # mstyle | 
| 1658 | 285 | 50 |  |  |  | 1439 | if ($tag eq 'mstyle') { | 
| 1659 | 285 |  |  |  |  | 1385 | @child_str = grep $_ ne '', @child_str; | 
| 1660 | 285 | 100 |  |  |  | 793 | if ($self->parent->{tag} eq 'math') { | 
| 1661 | 273 | 100 |  |  |  | 745 | push @child_str, ' ' unless @child_str; | 
| 1662 |  |  |  |  |  |  | # The top-level mstyle | 
| 1663 | 273 | 50 | 33 |  |  | 3553 | return (defined $self->{attr}{displaystyle} && | 
| 1664 |  |  |  |  |  |  | $self->{attr}{displaystyle} eq 'true') ? | 
| 1665 |  |  |  |  |  |  | "\$\$@child_str\$\$" : "\$@child_str\$"; | 
| 1666 |  |  |  |  |  |  | } | 
| 1667 |  |  |  |  |  |  | else { | 
| 1668 |  |  |  |  |  |  | # It better be a font changing command | 
| 1669 | 12 | 100 |  |  |  | 62 | return $child_str[0] if $self->{attr}{mathvariant}; | 
| 1670 | 6 |  |  |  |  | 33 | my ($attr) = map($self->{attr}{$_}, | 
| 1671 |  |  |  |  |  |  | grep $self->{attr}{$_}, | 
| 1672 |  |  |  |  |  |  | qw(fontweight fontfamily)); | 
| 1673 | 6 | 50 | 33 |  |  | 155 | return $attr && $LatexFont{$attr} ? | 
| 1674 |  |  |  |  |  |  | "$LatexFont{$attr}\{$child_str[0]}" : $child_str[0]; | 
| 1675 |  |  |  |  |  |  | } | 
| 1676 |  |  |  |  |  |  | } | 
| 1677 |  |  |  |  |  |  | } | 
| 1678 |  |  |  |  |  |  | } | 
| 1679 |  |  |  |  |  |  |  | 
| 1680 |  |  |  |  |  |  | # Returns the next sibling of a node | 
| 1681 |  |  |  |  |  |  | # Arguments: None | 
| 1682 |  |  |  |  |  |  | # Returns:   node object or undef | 
| 1683 |  |  |  |  |  |  | sub nextSibling { | 
| 1684 | 51 |  |  | 51 |  | 79 | my ($self) = @_; | 
| 1685 | 51 |  |  |  |  | 119 | my $parent = $self->parent; | 
| 1686 | 51 |  |  |  |  | 104 | for (my $i=0; $i<@{$parent->{children}}; $i++) { | 
|  | 64 |  |  |  |  | 194 |  | 
| 1687 | 64 | 100 |  |  |  | 379 | return $parent->{children}[$i+1] if $self eq $parent->{children}[$i]; | 
| 1688 |  |  |  |  |  |  | } | 
| 1689 | 0 |  |  |  |  | 0 | return $Null; | 
| 1690 |  |  |  |  |  |  | } | 
| 1691 |  |  |  |  |  |  |  | 
| 1692 |  |  |  |  |  |  | # Returns the tag of a node | 
| 1693 |  |  |  |  |  |  | # Arguments: None | 
| 1694 |  |  |  |  |  |  | # Returns:   string | 
| 1695 |  |  |  |  |  |  | sub nodeName : method { | 
| 1696 | 1098 |  | 100 | 1098 |  | 6707 | return $_[0]{tag} || ''; | 
| 1697 |  |  |  |  |  |  | } | 
| 1698 |  |  |  |  |  |  |  | 
| 1699 |  |  |  |  |  |  | # Returns the text of a text node | 
| 1700 |  |  |  |  |  |  | # Arguments: None | 
| 1701 |  |  |  |  |  |  | # Returns:   string | 
| 1702 |  |  |  |  |  |  | sub nodeValue : method { | 
| 1703 | 910 |  | 100 | 910 |  | 4158 | return $_[0]{text} || ''; | 
| 1704 |  |  |  |  |  |  | } | 
| 1705 |  |  |  |  |  |  |  | 
| 1706 |  |  |  |  |  |  | # Returns the parent of a node | 
| 1707 |  |  |  |  |  |  | # Arguments: none | 
| 1708 |  |  |  |  |  |  | # Returns:   parent node object or undef | 
| 1709 |  |  |  |  |  |  | sub parent : method { | 
| 1710 | 970 |  | 33 | 970 |  | 10665 | return $Parent{$_[0]} || $Null; | 
| 1711 |  |  |  |  |  |  | } | 
| 1712 |  |  |  |  |  |  |  | 
| 1713 |  |  |  |  |  |  | # Returns the previous sibling of a node | 
| 1714 |  |  |  |  |  |  | # Arguments: None | 
| 1715 |  |  |  |  |  |  | # Returns:   node object or undef | 
| 1716 |  |  |  |  |  |  | sub previousSibling { | 
| 1717 | 11 |  |  | 11 |  | 16 | my ($self) = @_; | 
| 1718 | 11 |  |  |  |  | 25 | my $parent = $self->parent; | 
| 1719 | 11 |  |  |  |  | 24 | for (my $i=1; $i<@{$parent->{children}}; $i++) { | 
|  | 12 |  |  |  |  | 37 |  | 
| 1720 | 6 | 100 |  |  |  | 33 | return $parent->{children}[$i-1] if $self eq $parent->{children}[$i]; | 
| 1721 |  |  |  |  |  |  | } | 
| 1722 | 6 |  |  |  |  | 14 | return $Null; | 
| 1723 |  |  |  |  |  |  | } | 
| 1724 |  |  |  |  |  |  |  | 
| 1725 |  |  |  |  |  |  | # Removes a given child node from a node | 
| 1726 |  |  |  |  |  |  | # Arguments: child node | 
| 1727 |  |  |  |  |  |  | # Returns:   None | 
| 1728 |  |  |  |  |  |  | # Side-effects: May affect children of the node | 
| 1729 |  |  |  |  |  |  | sub removeChild : method { | 
| 1730 | 626 |  |  | 626 |  | 1230 | my ($self, $child) = @_; | 
| 1731 | 626 | 50 |  |  |  | 1618 | @{$self->{children}} = grep $_ ne $child, @{$self->{children}} | 
|  | 626 |  |  |  |  | 2234 |  | 
|  | 626 |  |  |  |  | 7991 |  | 
| 1732 |  |  |  |  |  |  | if $self->{children}; | 
| 1733 | 626 |  |  |  |  | 3136 | delete $Parent{$child}; | 
| 1734 |  |  |  |  |  |  | } | 
| 1735 |  |  |  |  |  |  |  | 
| 1736 |  |  |  |  |  |  | # Replaces one child node object with another | 
| 1737 |  |  |  |  |  |  | # Arguments: old child node object, new child node object | 
| 1738 |  |  |  |  |  |  | # Returns:   None | 
| 1739 |  |  |  |  |  |  | sub replaceChild : method { | 
| 1740 | 30 |  |  | 30 |  | 65 | my ($self, $new, $old) = @_; | 
| 1741 | 30 | 100 |  |  |  | 40 | @{$self->{children}} = map $_ eq $old ? $new : $_, @{$self->{children}}; | 
|  | 30 |  |  |  |  | 8342 |  | 
|  | 30 |  |  |  |  | 248 |  | 
| 1742 | 30 |  |  |  |  | 659 | delete $Parent{$old}; | 
| 1743 | 30 |  |  |  |  | 213 | $Parent{$new} = $self; | 
| 1744 |  |  |  |  |  |  | } | 
| 1745 |  |  |  |  |  |  |  | 
| 1746 |  |  |  |  |  |  | # Sets one or more attributes on a node object | 
| 1747 |  |  |  |  |  |  | # Arguments: set of attribute/value pairs | 
| 1748 |  |  |  |  |  |  | # Returns:   None | 
| 1749 |  |  |  |  |  |  | sub setAttribute : method { | 
| 1750 | 892 |  |  | 892 |  | 1406 | my $self = shift; | 
| 1751 | 892 | 50 |  |  |  | 3195 | if (@_) { | 
| 1752 | 892 | 50 |  |  |  | 3142 | $self->{attr} = {} unless $self->{attr}; | 
| 1753 | 892 | 50 |  |  |  | 3558 | $self->{attrlist} = [] unless $self->{attrlist}; | 
| 1754 |  |  |  |  |  |  | } | 
| 1755 | 892 |  |  |  |  | 4462 | while (my($aname, $aval) = splice(@_, 0, 2)) { | 
| 1756 | 1988 |  |  |  |  | 3480 | $aval =~ s/\n//g; | 
| 1757 | 1988 | 50 |  |  |  | 6110 | push @{$self->{attrlist}}, $aname unless defined $self->{attr}{$aname}; | 
|  | 1988 |  |  |  |  | 4580 |  | 
| 1758 | 1988 |  |  |  |  | 12232 | $self->{attr}{$aname} = $aval; | 
| 1759 |  |  |  |  |  |  | } | 
| 1760 |  |  |  |  |  |  | } | 
| 1761 |  |  |  |  |  |  |  | 
| 1762 |  |  |  |  |  |  | # Returns the ASCII representation of a node object | 
| 1763 |  |  |  |  |  |  | # Arguments: None | 
| 1764 |  |  |  |  |  |  | # Returns:   Text string | 
| 1765 |  |  |  |  |  |  | sub text : method { | 
| 1766 | 3697 |  |  | 3697 |  | 4232 | my ($self) = @_; | 
| 1767 | 3697 | 100 |  |  |  | 19475 | return $self->{text} if defined $self->{text}; | 
| 1768 | 2400 |  |  |  |  | 3576 | my $tag = $self->{tag}; | 
| 1769 | 2400 |  |  |  |  | 7578 | my $attr = join '', map(" $_=\"" . | 
| 1770 |  |  |  |  |  |  | ($_ eq 'xmlns' ? $self->{attr}{$_} : | 
| 1771 |  |  |  |  |  |  | Text::ASCIIMathML::_xml_encode($self->{attr}{$_})) . | 
| 1772 | 2400 | 50 |  |  |  | 4506 | "\"", @{$self->{attrlist}}) | 
|  |  | 50 |  |  |  |  |  | 
| 1773 |  |  |  |  |  |  | if $tag; | 
| 1774 | 2400 | 100 |  |  |  | 3120 | if (@{$self->{children}}) { | 
|  | 2400 |  |  |  |  | 5736 |  | 
| 1775 | 2378 |  |  |  |  | 9058 | my $child_str; | 
| 1776 | 2378 |  |  |  |  | 2891 | foreach (@{$self->{children}}) { | 
|  | 2378 |  |  |  |  | 4696 |  | 
| 1777 | 3423 |  |  |  |  | 8414 | $child_str .= $_->text; | 
| 1778 |  |  |  |  |  |  | } | 
| 1779 | 2378 | 50 |  |  |  | 14023 | return $tag ? "<$tag$attr>$child_str$tag>" : $child_str; | 
| 1780 |  |  |  |  |  |  | } | 
| 1781 | 22 | 50 |  |  |  | 99 | return $tag ? "<$tag$attr/>" : ''; | 
| 1782 |  |  |  |  |  |  | } | 
| 1783 |  |  |  |  |  |  | } | 
| 1784 |  |  |  |  |  |  |  | 
| 1785 |  |  |  |  |  |  | 1; |