1 # -*- coding: UTF-8 -*-
3 from copy
import deepcopy
5 from xml
.etree
.ElementTree
import Element
, tostring
6 import xml
.etree
.ElementTree
as etree
8 class InvalidAsciiMath(Exception):
10 handle invalid Ascii Math
23 'varepsilon': u
'\u025B',
41 'vartheta': u
'\u03D1',
51 'and':'and', # space = true
52 'or' : 'or', # space = true
53 'if' :'if', # space = true
56 symbol_dict
.update(greek_dict
)
149 "quad": u
"\u00A0\u00A0",
150 "qquad": u
"\u00A0\u00A0\u00A0\u00A0",
154 "diamond": u
"\u22C4",
170 # left off f and g because don't know what to do with them
173 '(':{'type':'special'},
174 '{':{'type':'special'},
175 '}':{'type':'special'},
176 ')':{'type':'special'},
177 '[':{'type':'special'},
178 ']':{'type':'special'},
179 '/':{'type':'special'},
180 '^':{'type':'special'},
181 '_':{'type':'special'},
182 '|':{'type':'special'},
183 '||':{'type':'special'},
184 '(:':{'type':'special'},
185 ':)':{'type':'special'},
186 '<<':{'type':'special'},
187 '>>':{'type':'special'},
188 '{:':{'type':'special'},
189 ':}':{'type':'special'},
190 'hat':{'type':'special'},
191 'bar':{'type':'special'},
192 'vec':{'type':'special'},
193 'dot':{'type':'special'},
194 'ddot':{'type':'special'},
195 'ul':{'type':'special'},
196 'root':{'type':'special'},
197 'stackrel':{'type':'special'},
198 'frac':{'type':'special'},
199 'sqrt':{'type':'special'},
200 'text':{'type':'special'},
204 under_over_list
= [u
"\u2211", u
"\u220f", u
"\u22c0", u
"\u22c1",u
"\u22c2",u
"\u22c3", "min", "max", "Lim", "lim"]
205 under_over_base_last
= ['hat', 'bar', 'vec', 'dot', 'ddot', 'ul']
206 over_list
= ['hat', 'bar', 'vec', 'dot', 'ddot']
208 fence_list
= ['(', ')', '{', '}', '[', ']', u
'\u2239', u
'\u232a', '(:', ':)', '<<', '>>', '{:', ':}']
209 open_fence_list
= ['(', '{', '[', u
'\u2329', '<<', '{:']
210 close_fence_list
= [')', '}', ']', u
'\u232A', '>>', ':}']
211 function_list
= ['root', 'stackrel', 'frac', 'sqrt']
212 group_func_list
= ['min', 'max', 'sin', 'cos', 'tan', 'sinh', 'cosh', 'tanh', 'cot', 'sec', 'csc', 'log', 'ln', 'det', 'gcd', 'lcm']
213 fence_pair
= {')':'(', '}':'{', ']':'[', u
'\u232A':u
'\u2329', ':}': '{:'} # last pair goes first in this dic
214 over_dict
= {'hat':'^', 'bar':u
"\u00AF", 'vec':u
"\u2192", 'dot':u
".", 'ddot':u
".."}
215 under_dict
= {'ul': u
"\u0332"}
216 sym_list
= symbol_dict
.keys()
217 spec_name_list
= special_dict
.keys()
218 op_name_list
= operator_dict
.keys()
219 text_list
= text_dict
.keys()
220 names
= sorted(sym_list
+ op_name_list
+ spec_name_list
+ text_list
, key
=lambda key_string
: len(key_string
), reverse
=True)
222 def __init__(self
, output_encoding
= 'utf8'):
223 self
._number
_re
= re
.compile('-?(\d+\.(\d+)?|\.?\d+)')
224 self
._tree
= Element('math')
225 mstyle
= etree
.SubElement(self
._tree
, 'mstyle')
226 self
._mathml
_ns
= 'http://www.w3.org/1998/Math/MathML'
227 self
._append
_el
= mstyle
228 self
._output
_encoding
= output_encoding
229 self
._fenced
_for
_right
= False # used fence for right fence with no match
230 self
._fenced
_for
_left
= False # used fence for left fence with no match
231 self
._use
_fence
= True # use <mfence> for fences
233 def _add_namespace(self
):
234 attributes
= self
._tree
.attrib
235 value
= attributes
.get('xmlns')
237 self
._tree
.set('xmlns', self
._mathml
_ns
)
239 def to_xml_string(self
, encoding
=None):
241 encoding
= self
._output
_encoding
242 self
._add
_namespace
()
243 xml_string
= tostring(self
._tree
, encoding
=encoding
)
247 self
._add
_namespace
()
250 def _make_element(self
, tag
, text
=None, *children
, **attrib
):
251 element
= Element(tag
, **attrib
)
254 if isinstance(text
, basestring
):
257 children
= (text
, ) + children
259 for child
in children
:
260 element
.append(child
)
264 def _change_el_name(self
, element
, new_name
):
265 element
.tag
= new_name
268 def _get_previous_sibling(self
, element
, the_tree
= 0):
270 either the previous sibling passed to the function, or if none is passed,
271 the previous sibling of the last element written
275 the_tree
= self
._tree
276 parent
= self
._get
_parent
(child
= element
, the_tree
= the_tree
)
285 return parent
[counter
- 1]
287 def _get_last_element(self
):
288 if len(self
._append
_el
) > 0:
289 return self
._append
_el
[-1]
290 return self
._append
_el
292 def _get_following_sibling(self
, element
, the_tree
= 0):
294 the_tree
= self
._tree
295 parent
= self
._get
_parent
(the_tree
= the_tree
, child
= element
)
302 if len(parent
) == counter
+ 1:
304 return parent
[counter
+ 1]
306 def _get_parent(self
, child
, the_tree
= 0):
309 the_tree: an xml.etree of the whole tree
311 child: an xml.etree of the child element
313 There is no direct way to get the parent of an element in etree. This
314 method makes a child-parent dictionary of the whold tree, than accesses
319 the_tree
= self
._tree
320 child_parent_map
= dict((c
, p
) for p
in the_tree
.getiterator() for c
in p
)
321 parent
= child_parent_map
.get(child
)
324 def _get_grandparent(self
, child
, the_tree
= 0):
325 parent
= self
._get
_parent
(child
= child
, the_tree
= the_tree
)
326 grandparent
= self
._get
_parent
(child
= parent
, the_tree
= the_tree
)
328 def _change_element(self
, element
, name
, **attributes
):
330 Changes just the top element to the name "element' with the **attributes passed to it.
334 the_keys
= element
.attrib
.keys()
335 for the_key
in the_keys
:
336 del(element
.attrib
[the_key
])
337 for att
in attributes
:
338 element
.set(att
, attributes
[att
])
341 def _fix_open_fence(self
, element
):
343 changes <mfence open="(" close=""
352 parent
= self
._get
_parent
(element
)
353 the_open
= element
.get('open')
361 paren
= self
._make
_element
('mo', text
= the_open
)
362 parent
.insert(position
, paren
)
367 parent
.remove(element
)
369 def _insert_mrow(self
, element
, class_name
):
371 Inserts an mrow element around element
374 if len(element
) == 1:
376 self
._change
_element
(element
, 'mrow', **{'class':class_name
})
377 new_element
= deepcopy(element
)
378 self
._append
_el
.remove(element
)
379 parenthesis
= self
._make
_element
('mfenced', open='(', separators
='', close
=")")
380 parenthesis
.append(new_element
)
381 self
._append
_el
.append(parenthesis
)
383 def _count_commas(self
, element
):
385 counts commas for matrix
390 for child
in element
:
391 if child
.tag
== 'mo' and child
.text
== ',':
395 def _is_matrix(self
, element
):
397 Tests if element is in fact a matrix
402 if not self
._is
_full
_fenced
(element
):
406 for child
in element
:
407 if counter
% 2 == 0: # even
408 if not self
._is
_full
_fenced
(child
):
410 num_commas
= self
._count
_commas
(child
)
414 if num_commas
!= row_len
:
418 if child
.tag
!= 'mo' or child
.text
!= ',':
424 def _is_full_fenced(self
, element
):
426 Returns True if element is a fence with matching open and close; or if open is {: or closse is :}
431 close_fence
= element
.get('close')
432 the_class
= element
.get('class')
433 open_fence
= element
.get('open')
434 if close_fence
== u
'\u232A': # don't remove these parenthesis
436 pair
= self
.fence_pair
.get(close_fence
)
437 if the_class
== 'invisible':
441 if element
.tag
== 'mfenced':
445 def _add_num_to_tree(self
, token
, the_type
):
446 element
= self
._make
_element
('mn', text
=token
)
447 self
._append
_el
.append(element
)
450 def _add_text_to_tree(self
, text
):
451 present_text
= self
._append
_el
.text
452 if present_text
== None:
454 self
._append
_el
.text
= present_text
+ text
456 def _add_text_el_to_tree(self
):
457 element
= self
._make
_element
('mtext')
458 self
._append
_el
.append(element
)
459 self
._append
_el
= element
461 def _end_text_el_to_tree(self
):
462 self
._append
_el
.attrib
.pop('open')
463 self
._append
_el
= self
._get
_parent
(self
._append
_el
)
465 def _add_special_text_to_tree(self
, text
):
470 element
= self
._make
_element
('mspace', **{'width':'1ex'})
471 self
._append
_el
.append(element
)
472 element
= self
._make
_element
('mo', text
=text
)
473 self
._append
_el
.append(element
)
474 element
= self
._make
_element
('mspace', **{'width':'1ex'})
475 self
._append
_el
.append(element
)
477 def _add_neg_num_to_tree(self
, token
, the_type
):
478 groups
= ['msup', 'msub', 'munderover', 'munder', 'mover', 'mroot', 'msqrt', 'mfrac']
479 if self
._append
_el
.tag
in groups
:
480 element
= self
._make
_element
('mrow', **{'class':'neg-num'})
481 self
._append
_el
.append(element
)
484 append_el
= self
._append
_el
486 element
= self
._make
_element
('mo', text
='-')
487 append_el
.append(element
)
488 element
= self
._make
_element
('mn', text
=num
)
489 append_el
.append(element
)
491 def _add_alpha_to_tree(self
, token
, the_type
):
492 element
= self
._make
_element
('mi', text
=token
)
493 self
._append
_el
.append(element
)
495 def _add_symbol_to_tree(self
, token
, token_dict
):
496 token
= token_dict
['symbol']
497 element
= self
._make
_element
('mi', text
=token
)
498 self
._append
_el
.append(element
)
500 def _add_operator_to_tree(self
, token
, token_info
):
501 if isinstance(token_info
, dict):
502 text
= token_info
.get('symbol')
505 element
= self
._make
_element
('mo', text
=text
)
506 self
._append
_el
.append(element
)
508 def _do_matrix(self
):
509 last_element
= self
._get
_last
_element
()
510 is_matrix
= self
._is
_matrix
(last_element
)
513 the_open
= last_element
.get('open')
514 close
= last_element
.get('close')
515 the_class
= last_element
.get('class')
516 the_dict
= {'open': the_open
, 'close': close
, 'separators':''}
518 the_dict
['class'] = the_class
519 fenced
= self
._make
_element
('mfenced', **the_dict
)
520 table
= self
._make
_element
('mtable')
522 for child
in last_element
:
523 if self
._is
_full
_fenced
(child
):
524 row
= self
._make
_element
('mtr')
526 cell
= self
._make
_element
('mtd')
529 if gc
.tag
!= 'mo' or gc
.text
!= ',':
532 cell
= self
._make
_element
('mtd')
534 self
._append
_el
.remove(last_element
)
535 self
._append
_el
.append(fenced
)
538 def _handle_binary(self
, token
, info
):
539 last_element
= self
._get
_last
_element
()
540 if last_element
== self
._append
_el
: # no "previous sibling," and can't process
541 self
._add
_operator
_to
_tree
(token
, info
)
545 if last_element
.tag
== 'mfrac':
546 for child
in last_element
:
547 if child
.tag
== 'mfrac':
549 if num_frac
% 2 != 0:
550 self
._append
_el
= last_element
551 last_element
= self
._get
_last
_element
()
552 if self
._is
_full
_fenced
(last_element
):
553 self
._change
_element
(last_element
, 'mrow', **{'class':'nominator'})
554 nominator
= deepcopy(last_element
)
555 self
._append
_el
.remove(last_element
)
556 mfrac
= self
._make
_element
('mfrac', nominator
)
557 self
._append
_el
.append(mfrac
)
558 self
._append
_el
= mfrac
559 elif token
== '^' or token
== '_':
560 if last_element
.tag
== 'msub' or last_element
.tag
== 'munder':
561 if last_element
.tag
== 'msub':
562 new_element
= self
._make
_element
('msubsup')
564 new_element
= self
._make
_element
('munderover')
565 for child
in last_element
: # should be just 2--check?
566 element
= deepcopy(child
)
567 new_element
.append(element
)
568 self
._append
_el
.remove(last_element
)
569 self
._append
_el
.append(new_element
)
570 self
._append
_el
= new_element
573 if last_element
.text
in self
.under_over_list
and token
== '^':
575 elif last_element
.text
in self
.under_over_list
and token
== '_':
581 base
= deepcopy(last_element
)
582 self
._append
_el
.remove(last_element
)
583 base
= self
._make
_element
(el_name
, base
)
584 self
._append
_el
.append(base
)
585 self
._append
_el
= base
588 def _handle_open_fence(self
, token
):
590 element
= self
._make
_element
('mfenced', open=token
, separators
='', close
="")
592 element
= self
._make
_element
('mo', text
=token
)
593 self
._append
_el
.append(element
)
594 self
._append
_el
= element
596 def _handle_close_fence(self
, token
):
597 first_match
= self
.fence_pair
.get(token
)
598 element
= self
._append
_el
600 while element
!= None:
601 if element
.tag
== 'mfenced' and element
.get('open') == first_match
:
602 element
.set('close', token
)
603 parent
= self
._get
_parent
(element
)
604 self
._append
_el
= parent
607 elif element
.tag
== 'mfenced' and ( element
.get('open') == '{:' or token
== ':}'):
608 element
.set('class','invisible')
609 parent
= self
._get
_parent
(element
)
610 self
._append
_el
= parent
613 element
= self
._get
_parent
(element
)
617 if self
._fenced
_for
_right
:
618 element
= self
._make
_element
('mfenced', open='', separators
='', close
=token
)
619 self
._append
_el
.append(element
)
621 element
= self
._make
_element
('mo', text
=token
)
622 self
._append
_el
.append(element
)
624 def _handle_close_fence_old(self
, token
):
625 the_open
= self
._append
_el
.get('open')
626 first_match
= self
.fence_pair
.get(token
)
627 if self
._append
_el
.tag
== 'mfenced' and the_open
== first_match
:
628 self
._append
_el
.set('close', token
)
629 parent
= self
._get
_parent
(self
._append
_el
)
630 self
._append
_el
= parent
631 elif self
._append
_el
.tag
== 'mfenced' and ( the_open
== '{:' or token
== ':}'):
632 self
._append
_el
.set('class','invisible')
633 parent
= self
._get
_parent
(self
._append
_el
)
634 self
._append
_el
= parent
636 self
._find
_match
_fence
(self
._append
_el
, token
)
637 if self
._fenced
_for
_right
:
638 element
= self
._make
_element
('mfenced', open='', separators
='', close
=token
)
639 self
._append
_el
.append(element
)
641 element
= self
._make
_element
('mo', text
=token
)
642 self
._append
_el
.append(element
)
644 def _find_match_fence(self
, element
, close
):
645 first_match
= self
.fence_pair
.get(close
)
646 while element
!= None:
648 if element
.tag
== 'mfenced' and element
.get('open') == first_match
:
649 self
._append
_el
= element
650 element
.set('close', close
)
651 parent
= self
._get
_parent
(element
)
652 self
._append
_el
= parent
654 element
= self
._get
_parent
(element
)
656 def _handle_double_single_bar(self
, token
, the_type
):
662 if self
._append
_el
.tag
== 'mfenced' and self
._append
_el
.get('open') == the_chr
:
663 self
._append
_el
.set('close', the_chr
)
664 parent
= self
._get
_parent
(self
._append
_el
)
665 self
._append
_el
= parent
667 element
= self
._make
_element
('mfenced', open=the_chr
, separators
='', close
="")
668 self
._append
_el
.append(element
)
669 self
._append
_el
= element
672 def _handle_over(self
, token
):
673 element
= self
._make
_element
('mover', **{'class':token
} )
674 self
._append
_el
.append(element
)
675 self
._append
_el
= element
677 def _handle_under(self
, token
):
678 element
= self
._make
_element
('munder', **{'class':token
} )
679 self
._append
_el
.append(element
)
680 self
._append
_el
= element
682 def _handle_function(self
, token
):
684 element
= self
._make
_element
('mroot')
685 self
._append
_el
.append(element
)
686 self
._append
_el
= element
687 elif token
== 'stackrel':
688 element
= self
._make
_element
('mover', **{'class':'stackrel'})
689 self
._append
_el
.append(element
)
690 self
._append
_el
= element
691 elif token
== 'frac':
692 element
= self
._make
_element
('mfrac')
693 self
._append
_el
.append(element
)
694 self
._append
_el
= element
695 elif token
== 'sqrt':
696 element
= self
._make
_element
('msqrt')
697 self
._append
_el
.append(element
)
698 self
._append
_el
= element
700 def _add_special_to_tree(self
, token
, the_type
):
701 if token
in self
.open_fence_list
:
702 self
._handle
_open
_fence
(token
)
703 elif token
in self
.close_fence_list
:
704 self
._handle
_close
_fence
(token
)
705 elif token
== '/' or token
== '^' or token
== '_':
706 self
._handle
_binary
(token
, the_type
)
707 elif token
== '||' or token
== '|':
708 self
._handle
_double
_single
_bar
(token
, the_type
)
710 self
._handle
_single
_bar
(token
, the_type
)
711 elif token
in self
.over_list
:
712 self
._handle
_over
(token
)
713 elif token
in self
.under_list
:
714 self
._handle
_under
(token
)
715 elif token
in self
.function_list
:
716 self
._handle
_function
(token
)
718 def _add_fence_to_tree(self
, token
, the_type
):
719 if token
== '(:' or token
== '<<':
721 if token
== ':)' or token
== '>>':
723 if token
in self
.open_fence_list
:
724 self
._handle
_open
_fence
(token
)
725 elif token
in self
.close_fence_list
:
726 self
._handle
_close
_fence
(token
)
729 for e
in self
._tree
.getiterator('mfenced'):
730 if e
.get('close') == '' and e
.get('class') != 'invisible' and not self
._fenced
_for
_left
:
731 self
._fix
_open
_fence
(e
)
732 for e
in self
._tree
.getiterator():
733 if e
.tag
== 'mfrac' and len(e
) != 2:
734 element
= self
._make
_element
('mo')
736 element
= self
._make
_element
('mo')
737 e
.insert(len(e
), element
)
738 elif e
.tag
== 'mroot' and len(e
) != 2:
740 element
= self
._make
_element
('mo')
744 element
= self
._make
_element
('mo')
745 e
.insert(len(e
), element
)
746 elif (e
.tag
== 'mover' or e
.tag
== 'munder') and len(e
) != 2:
747 char
= self
.over_dict
.get(e
.get('class'))
749 char
= self
.under_dict
.get(e
.get('class'))
751 element
= self
._make
_element
('mo')
753 element
= self
._make
_element
('mo', text
=char
)
756 element
= self
._make
_element
('mo')
758 elif (e
.tag
== 'munderover' or e
.tag
== 'msubsup') and len(e
) != 3:
759 element
= self
._make
_element
('mo')
761 element
= self
._make
_element
('mo')
762 e
.insert(len(e
), element
)
763 elif (e
.tag
== 'msup' or e
.tag
== 'msub') and len(e
) != 2:
764 element
= self
._make
_element
('mo')
766 element
= self
._make
_element
('mo')
767 e
.insert(len(e
), element
)
770 def parse_string(self
, the_string
):
776 while the_string
!= '':
777 the_string
, token
, token_info
= self
._parse
_tokens
(the_string
)
778 if isinstance(token_info
, str):
779 the_type
= token_info
781 the_type
= token_info
.get('type')
782 if the_type
== 'text':
784 self
._add
_text
_to
_tree
(text
)
785 elif the_type
== 'start_text':
786 self
._add
_text
_el
_to
_tree
()
787 elif the_type
== 'end_text':
788 self
._end
_text
_el
_to
_tree
()
789 elif the_type
== 'special_text':
790 self
._add
_special
_text
_to
_tree
(token
)
791 elif the_type
== 'number':
792 self
._add
_num
_to
_tree
(token
, the_type
)
793 elif the_type
== 'neg_number':
794 self
._add
_neg
_num
_to
_tree
(token
, the_type
)
795 elif the_type
== 'alpha':
796 self
._add
_alpha
_to
_tree
(token
, the_type
)
797 elif the_type
== 'symbol':
798 self
._add
_symbol
_to
_tree
(token
, token_info
)
799 elif the_type
== 'operator':
800 self
._add
_operator
_to
_tree
(token
, token_info
)
801 elif token
in self
.fence_list
:
802 self
._add
_fence
_to
_tree
(token
, the_type
)
803 elif the_type
== 'special':
804 self
._add
_special
_to
_tree
(token
, the_type
)
806 if self
._append
_el
.tag
== 'mover' and self
._append
_el
.get('class') == 'stackrel' and len(self
._append
_el
) == 2:
807 if self
._is
_full
_fenced
(self
._append
_el
[0]):
808 self
._change
_element
(self
._append
_el
[0], 'mrow', **{'class':'top'})
809 if self
._is
_full
_fenced
(self
._append
_el
[1]):
810 self
._change
_element
(self
._append
_el
[1], 'mrow', **{'class':'bottom'})
811 top
= deepcopy(self
._append
_el
[0])
812 self
._append
_el
[0] = self
._append
_el
[1]
813 self
._append
_el
[1] = top
814 self
._append
_el
= self
._get
_parent
(self
._append
_el
)
815 elif (self
._append
_el
.tag
== 'mover' or self
._append
_el
.tag
== 'munder')\
816 and self
._append
_el
.get('class') in self
.under_over_base_last
and len(self
._append
_el
) > 0:
817 last_element
= self
._get
_last
_element
()
818 if self
._is
_full
_fenced
(last_element
): # remove parenthesis
819 if self
._append
_el
.tag
== 'mover':
820 the_dict
= {'class':'mover'}
821 if self
._append
_el
.tag
== 'munder':
822 the_dict
= {'class':'munder'}
823 self
._change
_element
(last_element
, 'mrow', **the_dict
)
824 text
= self
._append
_el
.get('class') # add top
825 if self
._append
_el
.tag
== 'mover':
826 text
= self
.over_dict
.get(text
)
827 elif self
._append
_el
.tag
== 'munder':
828 text
= self
.under_dict
.get(text
)
829 element
= self
._make
_element
('mo', text
=text
)
830 self
._append
_el
.append(element
)
831 self
._append
_el
= self
._get
_parent
(self
._append
_el
)
832 elif self
._append
_el
.tag
== 'msqrt' and len(self
._append
_el
) == 1:
833 if self
._is
_full
_fenced
(self
._append
_el
[0]):
834 self
._change
_element
(self
._append
_el
[0], 'mrow', **{'class':'radical'})
835 self
._append
_el
= self
._get
_parent
(self
._append
_el
)
836 elif self
._append
_el
.tag
== 'mroot' and len(self
._append
_el
) == 2:
837 if self
._is
_full
_fenced
(self
._append
_el
[0]):
838 self
._change
_element
(self
._append
_el
[0], 'mrow', **{'class':'index'})
839 if self
._is
_full
_fenced
(self
._append
_el
[1]):
840 self
._change
_element
(self
._append
_el
[1], 'mrow', **{'class':'base'})
841 the_index
= deepcopy(self
._append
_el
[0])
842 self
._append
_el
[0] = self
._append
_el
[1]
843 self
._append
_el
[1] = the_index
844 self
._append
_el
= self
._get
_parent
(self
._append
_el
)
846 elif (self
._append
_el
.tag
== 'mfrac' or self
._append
_el
.tag
== 'msup' or\
847 self
._append
_el
.tag
== 'msub' or self
._append
_el
.tag
== 'munder'\
848 or self
._append
_el
.tag
== 'mover') and len(self
._append
_el
) == 2:
849 if self
._is
_full
_fenced
(self
._append
_el
[1]):
850 if self
._append
_el
.tag
== 'mfrac':
851 the_dict
= {'class':'denominator'}
852 elif self
._append
_el
.tag
== 'msup':
853 the_dict
= {'class':'superscript'}
854 elif self
._append
_el
.tag
== 'msub':
855 the_dict
= {'class':'subcript'}
856 elif self
._append
_el
.tag
== 'munder':
857 the_dict
= {'class':'munder'}
858 elif self
._append
_el
.tag
== 'mover':
859 the_dict
= {'class':'mover'}
860 self
._change
_element
(self
._append
_el
[1], 'mrow', **the_dict
)
861 self
._append
_el
= self
._get
_parent
(self
._append
_el
)
862 elif self
._append
_el
.tag
=='msubsup' or self
._append
_el
.tag
== 'munderover':
863 last_element
= self
._get
_last
_element
()
864 prev_sib
= self
._get
_previous
_sibling
(last_element
)
865 prev_prev_sib
=self
._get
_previous
_sibling
(prev_sib
)
866 if prev_prev_sib
!= None:
867 if self
._is
_full
_fenced
(last_element
):
868 if self
._append
_el
.tag
== 'msubsup':
869 the_dict
= {'class':'subsuper'}
871 the_dict
= {'class':'munderover'}
872 self
._change
_element
(last_element
, 'mrow', **the_dict
)
873 self
._append
_el
= self
._get
_parent
(self
._append
_el
)
875 last_element
= self
._get
_last
_element
()
876 if last_element
.tag
== 'mfenced' and last_element
.get('close') == ':}':
877 self
._change
_element
(last_element
, 'mrow', **{'class':'invisible'})
879 if self
._is
_full
_fenced
(last_element
) and len(self
._append
_el
)> 1 :
880 prev_sib
= self
._get
_previous
_sibling
(last_element
)
882 if prev_sib
.text
in self
.group_func_list
:
884 if prev_sib
.tag
== 'munderover':
885 if prev_sib
[0].tag
== 'mo' and prev_sib
[0].text
in self
.group_func_list
:
888 self
._insert
_mrow
(last_element
, 'function')
894 def _look_at_next_token(self
, the_string
):
895 the_string
, token
, the_type
= self
._parse
_tokens
(the_string
)
896 return the_string
, token
, the_type
899 def _parse_tokens(self
, the_string
):
903 processes the string one token at a time. If a number is found, process
904 and return the number with the rest of the stirng.
906 Else, see if the string starts with a special symbol, and process and
907 return that with the rest of the string.
909 Else, get the next character, and process that with the rest of the string.
912 if self
._append
_el
.tag
== 'mtext':
913 next_char
= the_string
[0]
914 if not self
._append
_el
.get('open'):
916 return the_string
[1:], ' ', {'type':'empty_text'}
917 elif next_char
== '(' or next_char
== '{' or next_char
== '[':
918 self
._append
_el
.set('open', next_char
)
919 return the_string
[1:], ' ', {'type': 'empty_text'}
920 else: # false text; continue
921 self
._append
_el
= self
._get
_parent
(self
._append
_el
)
923 first_match
= self
.fence_pair
.get(next_char
)
925 return the_string
[1:], next_char
, {'type':'end_text'}
927 return the_string
[1:], next_char
, {'type':'text'}
928 the_string
= the_string
.strip()
931 return None, None, None
933 match
= self
._number
_re
.match(the_string
)
935 if match
: # found a number
936 number
= match
.group(0)
938 return the_string
[match
.end():], number
, 'neg_number'
940 return the_string
[match
.end():], number
, 'number'
942 for name
in self
.names
:
943 if the_string
.startswith(name
):
944 the_found
= the_string
[:len(name
)]
945 symbol
= self
.symbol_dict
.get(the_found
)
946 operator
= self
.operator_dict
.get(the_found
)
947 special
= self
.special_dict
.get(the_found
)
948 text
= self
.text_dict
.get(the_found
)
949 if the_found
== 'text':
950 return the_string
[len(name
):], name
, {'type': 'start_text'}
952 return the_string
[len(name
):], name
, {'type': 'symbol', 'symbol': symbol
}
953 elif special
!= None:
954 return the_string
[len(name
):], name
, special
955 elif operator
!= None:
956 return the_string
[len(name
):], name
, {'type': 'operator', 'symbol': operator
}
958 return the_string
[len(name
):], name
, {'type': 'special_text'}
960 # found either an operator or a letter
962 if the_string
[0].isalpha():
963 return the_string
[1:], the_string
[0], 'alpha'
965 return the_string
[1:], the_string
[0], 'operator'
969 def ascii_to_xml_string(the_string
):
970 if isinstance(the_string
, str) and sys
.version_info
< (3,):
971 the_string
= the_string
.decode('utf8')
972 math_obj
= AsciiMathML()
973 math_obj
.parse_string(the_string
)
974 xml_string
= math_obj
.to_xml_string()
977 def ascii_to_math_tree(the_string
):
978 if isinstance(the_string
, str) and sys
.version_info
< (3,):
979 the_string
= the_string
.decode('utf8')
980 math_obj
= AsciiMathML()
981 math_obj
.parse_string(the_string
)
982 math_tree
= math_obj
.get_tree()