3 #include "TPushParser.hh"
4 #include "AMathMLFactory.hh"
6 TPushParser::TPushParser(ALogger& l, const TDictionary& d) : APushParser(l), dictionary(d)
11 TPushParser::TPushParser(ALogger& l, AMathMLFactory& f, const TDictionary& d) : APushParser(l, f), dictionary(d)
20 cursor = doc.create("cursor");
23 doc.root().append(cursor);
24 logger.verbosity(ALogger::Debug);
27 TPushParser::~TPushParser()
32 TPushParser::PRIME() const
34 const TDictionary::Entry entry = dictionary.find("prime");
35 if (entry.cls == TDictionary::OPERATOR) return entry.value;
40 TPushParser::do_begin()
42 TNode parent = cursor.parent();
43 if (parent.isC() && dictionary.find(parent.nameC()).table)
45 TNode row = doc.create("row");
46 TNode cell = doc.create("cell");
47 TNode g = doc.createG();
55 TNode g = doc.createG(nextId++);
64 TNode parent = cursor.parent();
65 if (parent && parent.isG() && parent.hasId())
67 // normal closing brace for an explicitly open group
71 else if (parent && parent.isG() && parent.parent() && parent.parent().is("cell"))
73 assert(!frames.empty());
74 // closing brace for a structure in which & or \cr have been used
75 TNode row = parent.parent().parent();
76 assert(row && row.is("row"));
77 TNode table = row.parent();
81 else if (parent && parent.isG() && !parent.hasId() && parent.parent() && !parent.parent().is("math"))
83 // closing brace for a right-open macro (like \over)
84 // It's not sure that we want to exit from a phantom group with a
85 // '}', because, to enter in the phantom group the user didn't insert a
92 // In this case, there is a redundant '}', so I think we can ignore it and
94 logger.warning("There is so no corresponding'{'");
100 TPushParser::do_shift()
102 TNode parent = cursor.parent();
104 if (parent.is("tex"))
106 TNode math = doc.create("math", nextId++);
107 TNode g = doc.createG();
108 cursor.replace(math);
112 else if (parent.isG() && !parent.hasId() && parent.parent() && parent.parent().is("math"))
116 // there is something before the cursor, hence this is the
117 // closing math shift
118 if (parent.parent()["display"] != "1")
120 // one math shift is enough to close it
125 // we need two closing math shifts
126 //cursor.remove(); ??
127 parent.parent().append(cursor);
130 else if (parent.parent()["display"] != "1")
132 // there is nothing before the cursor, and the math is not
133 // in display mode, so this must be a double math shift
134 parent.parent()["display"] = "1";
138 parent.parent().append(cursor);
141 else if (parent.is("math"))
147 // In TeX, the user can type
149 // In this case, '$' character is correct.
150 // I don't think so, if user types
151 // $12+{a-b\over2a+b$
152 // because the group is not closed with a '}'
153 logger.error("parser: math shift");
158 TPushParser::do_align()
160 TNode parent = cursor.parent();
161 if (parent && parent.isG() && parent.hasId())
163 // alignment tab used for the first time inside a group
164 TNode row = doc.create("row");
165 TNode cell = doc.create("cell");
166 TNode g = doc.createG();
169 g.append(parent.first(), cursor);
171 else if (parent && parent.isG() && parent.parent().is("cell"))
173 // alignment tab used within a cell
174 TNode oldCell = parent.parent();
175 assert(oldCell && oldCell.is("cell"));
176 TNode row = oldCell.parent();
177 assert(row && row.is("row"));
178 TNode cell = doc.create("cell");
179 if (oldCell.next()) oldCell.next().insert(cell);
180 else row.append(cell);
181 TNode g = doc.createG();
187 logger.error("alignment tab used outside matrix");
192 TPushParser::do_eol()
194 //if (cursor.parent()) cursor.remove();
198 TPushParser::do_parameter(const std::string& p)
204 TPushParser::do_subscript()
206 TNode parent = cursor.parent();
209 TNode prev = cursor.prev();
212 TNode elem = doc.create("sb", nextId++);
213 TNode g = doc.createG();
214 cursor.replace(elem);
220 TNode elem = doc.create("sb", nextId++);
226 else if (parent.isSb() && cursor == parent[1])
228 if (parent["under"] == "1") logger.error("already under");
229 else parent["under"] = "1";
234 TPushParser::do_superscript()
236 TNode parent = cursor.parent();
239 TNode prev = cursor.prev();
242 TNode elem = doc.create("sp", nextId++);
243 TNode g = doc.createG();
244 cursor.replace(elem);
250 TNode elem = doc.create("sp", nextId++);
256 else if (parent.isSp() && cursor == parent[1])
258 if (parent["over"] == "1") logger.error("already over");
259 else parent["over"] = "1";
264 TPushParser::do_space(const std::string&)
266 // ? may be used to distinguish tokens in some mode?
270 TPushParser::do_letter(const std::string& s)
272 //TNode parent = cursor.parent();
273 TNode elem = doc.createI(s, nextId++);
274 cursor.replace(elem);
279 TPushParser::do_digit(const std::string& s)
281 TNode parent = cursor.parent();
282 TNode prev = cursor.prev();
283 if (prev && parent.isG() && prev.is("n"))
285 TNode elem = doc.createN(prev.value() + s, nextId++);
290 TNode elem = doc.createN(s, nextId++);
291 cursor.replace(elem);
297 TPushParser::isPrimes(const TNode& node) const
300 return node.isG() && node.last() && node.last().is("o") && node.last()["val"] == PRIME();
304 TPushParser::do_apostrophe()
306 if (cursor.parent() && cursor.parent().isG())
308 if (TNode prev = cursor.prev())
310 if (prev.isSp() && prev[1] && isPrimes(prev[1]))
311 prev[1].append(doc.createO(PRIME(), nextId++));
312 else if (prev.isSb() && prev[0] &&
313 prev[0].isSp() && prev[0][1] &&
314 isPrimes(prev[0][1]))
315 prev[0][1].append(doc.createO(PRIME(), nextId++));
318 TNode elem = doc.create("sp");
319 TNode g = doc.createG();
323 g.append(doc.createO(PRIME(), nextId++));
329 logger.error("parser: you have to type one identifier before the ''");
339 TPushParser::do_other(const std::string& s)
347 /*cout << "TPushParser::do_other " << s << endl;
348 cout << "DOCUMENT: " << static_cast<GdomeNode*>(cursor.element().get_ownerDocument()) << endl;*/
349 TNode elem = doc.createT("o", s, nextId++);
350 cursor.replace(elem);
357 TPushParser::do_active(const std::string&)
363 TPushParser::do_comment()
371 TNode parent = cursor.parent();
372 if (parent && parent.isG() &&
373 parent.parent() && parent.parent().is("cell") &&
374 parent.parent().parent() && parent.parent().parent().is("row"))
376 TNode oldRow = parent.parent().parent();
378 TNode table = oldRow.parent();
380 TNode row = doc.create("row");
381 TNode cell = doc.create("cell");
382 TNode g = doc.createG();
383 if (oldRow.next()) oldRow.next().insert(row);
384 else table.append(row);
392 TPushParser::do_control(const std::string& name)
394 if (name == "cr") do_cr();
397 TNode parent = cursor.parent();
398 const TDictionary::Entry& entry = dictionary.find(name);
401 case TDictionary::IDENTIFIER:
403 TNode t = doc.createI(entry.value, nextId++);
409 case TDictionary::OPERATOR:
411 TNode t = doc.createO(entry.value, nextId++);
417 case TDictionary::NUMBER:
419 TNode t = doc.createN(entry.value, nextId++);
425 case TDictionary::MACRO:
427 TNode m = doc.createC(name, nextId++);
430 if (entry.leftOpen && entry.rightOpen)
432 assert(entry.pattern.empty());
433 assert(parent.isG());
434 TNode g1 = doc.createG();
435 g1["left-open"] = "1";
436 g1.append(parent.first(), m);
438 TNode g2 = doc.createG();
441 frames.push(Frame(entry));
443 else if (entry.leftOpen)
445 assert(parent.isG());
446 TNode g = doc.createG();
447 g["left-open"] = "1";
448 g.append(parent.first(), m);
452 else if (entry.rightOpen)
454 assert(entry.pattern.empty());
455 assert(parent.isG());
456 TNode g = doc.createG();
459 frames.push(Frame(entry));
461 else if (!entry.pattern.empty())
465 frames.push(Frame(entry));
467 if (entry.paramDelimited(0))
469 TNode g = doc.createG();
478 // error, but we could handle this very easily
479 logger.error(" parser:but we could handle this easily");
485 case TDictionary::UNDEFINED:
487 logger.error("parser: using undefined macro " + name);
488 TNode m = doc.createC(name, nextId++);
500 TPushParser::gdelete_prev_token()
502 assert(cursor.prev());
503 assert(cursor.parent());
504 TNode prev = cursor.prev();
505 assert(prev.is("i") || prev.is("o") || prev.is("n"));
507 // the control below is designed to handle the case in which val have more than one unicode character
508 DOM::UCS4String ucs4val(prev.element().getAttribute("val"));
509 if ((ucs4val.length() <= 1) || prev.element().hasAttribute("name"))
512 prev.replace(cursor);
514 if (cursor.parent().isC())
516 // in this case we have removed an element of a MACRO.
517 // we can assert that this element was a non delimited argument
518 assert(!frames.empty());
519 Frame& frame = frames.top();
520 assert(frame.pos > 0);
527 ucs4val.erase(ucs4val.length() - 1, 1);
528 prev.element().setAttribute(DOM::GdomeString("val"), DOM::GdomeString(ucs4val));
534 TPushParser::gdelete_prev_script()
536 // this method delete an sp or an sb preceding the cursor
537 assert(cursor.prev());
538 assert(cursor.parent());
539 TNode prev = cursor.prev();
540 assert(prev.is("sp") || prev.is("sb"));
543 // i can invoke the gdelet_prev, because a sp (sb) MUST have two children
548 TPushParser::gdelete_prev_group()
550 assert(cursor.prev() && cursor.prev().isG());
551 TNode prev = cursor.prev();
555 // a group may have no children, so the gdelete_prev is not appropriate
556 // so this method is not equivalent to the one above
561 TPushParser::gdelete_prev_macro()
563 assert(cursor.parent());
564 assert(cursor.prev());
565 TNode prev = cursor.prev();
568 const TDictionary::Entry& entry = dictionary.find(prev["name"]);
570 if (!entry.defined())
572 // We can assume tha the user want to completely delete the undefined macro
574 prev.replace(cursor);
578 // we start to remove a MACRO. Different actions must be taken, based on the nature
579 // of the MACRO. In some cases, we can't remove the MACRO immediately, in other
580 // cases it's correct. In the first set of cases, we have to update the stack, pushing
581 // a frame in it with a correct value of pos, in the
582 // second one, we must not push a frame in the stack
586 // In this fragment of code we also handle the leftOpen && rightOpen MACRO.
587 // if the control element is rightOpen, the cursor should be placed after
588 // the last child of the control element's last child, and than, we try to remove something.
589 // A frame MUST be pushed in the stack, because we dont' know if the following actions
590 // will completely remove the MACRO.
591 frames.push(Frame(entry));
593 // Since the MACRO is rightOpen, the last child of the MACRO must be a phantom group
594 assert(prev.last().isG() && !prev.last().hasId());
597 prev.last().append(cursor);
599 // the gdelete_prev is not appropriate, because the last child of the MACRO may have no children
600 do_gdelete_phantom_group();
602 else if (entry.leftOpen)
604 // the leftOpen MACRO MUST have one and only one child, which MUST be a phantom group
605 // In this case, we do not have to push a frame in the stack, because we remove the
606 // MACRO immediately, substituting it with the content of the phantom group.
607 // At the moment, we don't remove the last child of the phantom group, but
608 // i don't know if it's the correct behavior of the graphical deleting.
609 // To delete it, just remove the comment of the last instruction of this block
611 assert(prev.first());
612 assert(prev.first().isG());
613 assert(prev.first() == prev.last());
615 TNode g = prev.first();
617 prev.replace(g.first(), TNode());
620 else if (!entry.pattern.empty())
622 // we have to start to remove a MACRO which accepts arguments.
623 // If the MACRO accepts arguments, the MACRO has one or more children
624 assert(prev.size() >= 1);
626 // Differnt actions must be taken, based on the nature of the last child
627 // of the MACRO. We have to distinguish the case in which it's a delimited argument
628 // frome the one in which it's a not delimited argument.
629 if (prev.last().isG() && !prev.last().hasId())
631 // the last argument of the MACRO is a delimited argumet. We ideally remove
632 // the sequence of delimiters
634 prev.last().append(cursor);
635 // we have to push a frame with a correct value of pos
636 assert(entry.previousParam(entry.pattern.size()) != entry.pattern.size());
638 unsigned sequence_length = entry.pattern.size() - entry.previousParam(entry.pattern.size()) - 1;
639 unsigned p = entry.pattern.size() - sequence_length - 1;
640 // now, p is the correct value of pos, and we can push the frame.
641 frames.push(Frame(entry, p));
645 // in this case, the last child of the MACRO is not a delimited argument, so we try
646 // to remove it, but we have to take differnt actions if the MACRO is a table with rows or not.
648 if (entry.table == 1 && prev.last().is("row"))
650 // in this case the cursor should be appended to the group associated to
651 // the last cell of the last row of the table
652 assert(prev.last().last().is("cell") && prev.last().last().first().isG());
653 prev.last().last().first().append(cursor);
660 // we push a frame in the stack with a correct value of member pos.
661 // This correct value is the size of the pattern, because we have to START to delete
662 // a MACRO. It means that all of the MACRO's arguments have been inserted.
663 frames.push(Frame(entry, entry.pattern.size()));
667 // in this case we try to remove the last child of the MACRO.
672 // this block of code will never be executed, because the MACRO accepts arguments,
673 // so a MACRO's child MUST exist. But, we can handle this case, emitting a warning
674 logger.warning("Parser: the TML tree is in a strange state, but a good state will be generated");
677 } // end of the else of the if (prev.last().isG() && !prev.last().hasId())
679 } // end of if (!entry.pattern.empty())
682 // if we are here, the MACRO preceding the cursor, is !(rightOpen ||leftOpen),
683 // and has no pattern. It means that it has no children.
684 // We can replace it with the cursor
685 assert(prev.size() == 0);
687 prev.replace(cursor);
689 } // end of defined MACRO
694 TPushParser::gdelete_prev()
696 // if in this function, the prev of cursor does exist, also the parent and we want a graphical deleting.
698 assert(cursor.prev());
699 assert(cursor.parent());
701 TNode prev = cursor.prev();
703 if (prev.is("i") || prev.is("o") || prev.is("n"))
705 gdelete_prev_token();
707 else if (prev.isSp() || prev.isSb())
709 gdelete_prev_script();
713 gdelete_prev_group();
717 // here, we also treat the case in which the MACRO is a table
718 gdelete_prev_macro();
722 // not handled. Future cases...
728 TPushParser::rgreplace_father(void)
730 // this method MUST only be invoked, when the cursor
731 // is the only child of a group with id. This function
732 // replaces the group with the cursor. But if the new parent
733 // is a group with id and the cursor is the only child of the
734 // group, the new parent is replaced...and so on.
735 // r stands for recursive, g stands for graphical.
736 assert(cursor.parent());
737 assert(cursor.parent().isG() && cursor.parent().hasId());
739 TNode parent = cursor.parent();
741 while (parent.isG() && parent.hasId() && (parent.first() == cursor))
743 parent.replace(cursor);
744 parent = cursor.parent();
749 // in this case we have removed a MACRO's child.
750 // So, we have to update the member pos of the frame in the stack
751 // I can assert that this MACRO accepts arguments.
752 assert(!frames.empty());
753 Frame& frame = frames.top();
754 assert(frame.pos > 0);
760 TPushParser::do_gdelete_script()
762 // If we are here, the cursor is child of a script (sp or sb) and
763 // this means that a prev MUST exist and that there is one and only one
764 // element preceding the cursor. The sp's (or sb's) parent
765 // MUST NOT be a MACRO.
766 // The element preceding the cursor is the base of the script.
768 assert(cursor.parent() && (cursor.parent().isSp() || cursor.parent().isSb()));
769 TNode parent = cursor.parent();
771 assert(parent.size() == 2);
772 assert(parent.parent() && !parent.parent().isC());
774 TNode prev = cursor.prev();
776 if (prev.isG() /*&& !prev.hasId()*/ && (prev.size() == 0))
778 // in this case, the script's base is a group with no elements, so
779 // we have to remove the entire MACRO, replacing it with the cursor.
780 // This situation occurs when the user type something like this
787 parent.replace(cursor);
789 // if the new parent is a group with Id and the cursor is the only
790 // element of this group, we have to remove the group. This controls are made
791 // in the method rgreplace_father().
792 if (cursor.parent().isG() && cursor.parent().hasId()) rgreplace_father();
796 // in this case, the prev has to replace the script,
797 // and the cursor has to be placed after the prev.
798 assert(prev.hasId());
799 parent.replace(prev);
800 prev.parent().append(cursor);
801 // now prev have a preceding element
802 assert(cursor.parent().size() > 1);
805 } // end of method do_gdelet_script
808 TPushParser::do_gdelete_macro()
810 // If we are here, the cursor is a child of a MACRO and this means
811 // that there is an open frame for the control element
812 // and this element is closed at either side (no leftOpen no rightOpen)
813 // and the MACRO is waiting for a not delimited argument, so
814 // we can assert that frame.entry.pattern.size() >= 1
815 assert(cursor.parent() && cursor.parent().isC());
816 TNode parent = cursor.parent();
818 assert(!frames.empty());
819 Frame& frame = frames.top();
820 assert(frame.entry.pattern.size() >= 1);
822 // we have to take different actions, based on if a preceding element exists
824 TNode prev = cursor.prev();
827 // in this case, a prev does not exist, so the actions of deleting means
828 // that we have to remove the MACRO. So we have to pop the stack.
829 // Being here also means that the MACRO is waiting for the first argument
830 // (which is not delimited), but we don't mind about it.
831 assert(frame.pos == 0);
832 parent.replace(cursor);
835 // if the new parent is a group with Id, and has no elements other than the
836 // cursor, we can remove it, because it' a graphical deleting
837 if (cursor.parent() && cursor.parent().isG() && cursor.parent().hasId())
839 else if (cursor.parent().isC())
841 // We have assumed that a MACRO cannot be a MACRO's child.
842 // At the moment, this assumption is valid, but in a future
843 // it might be false.
844 assert(!frames.empty());
845 Frame& frame = frames.top();
846 assert(frame.pos > 0);
852 // a prev does exist, we have to control if it's a delimited argument or not.
853 if (prev.isG() && !prev.hasId())
855 // in this case, prev is a delimited argument, so we have
856 // to ideally remove the sequence of delimiters
857 Frame& frame = frames.top();
858 assert(frame.pos > 1);
861 assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
863 // these 3 lines of code update the member pos.
864 unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
865 assert(sequence_length);
866 frame.pos = frame.pos - sequence_length - 1;
870 // the prev is not a delimited argument, so we have to try to remove it.
871 // We "try", because the prev might be a something that
872 // a simple delete cannot remove completely
880 TPushParser::do_gdelete_groupId()
882 // if we are here, the cursor's parent is a group with Id
883 assert(cursor.parent() && cursor.parent().isG() && cursor.parent().hasId());
884 TNode parent = cursor.parent();
886 // we have to take different actions based on if the cursor has a preceding
888 TNode prev = cursor.prev();
891 // the cursor has a preceding element, so we try to remove it
894 // We control if the group has to be removed, because the cursor
895 // might be the only element of the group.
896 if ((parent.first() == cursor) && parent.isG() && parent.hasId())
902 // the cursor has no preceding elements, so we have to remove the
906 // we have to re-start the process, because it' a graphical delete
910 } // end of method do_gdelete_groupId()
913 TPushParser::do_gdelete_phantom_group()
915 // if we are here, the cursor MUST be a child of a
917 assert(cursor.parent() && cursor.parent().isG() && !cursor.parent().hasId());
919 TNode parent = cursor.parent();
921 // now we have to control if the cursor has a preceding element or not
922 TNode prev = cursor.prev();
925 // the cursor has a preceding element, so we try to remove it
928 if (parent.size() == 1 && parent.parent().isSp())
930 // in this case the gdelete_prev has removed the only element preceding the cursor.
931 // If the phantom group is an sp's child, it means that the user has removed all \' in the
933 // We can remove the phamtom group and the sp element. But we also can only remove the
934 // phantom group, giving to the user the possibility of inserting an exponent.
935 // At the moment, we remove the sp element (and the phantom group), because it may be the correct
936 // behavior of a graphical deleting.
938 parent.replace(cursor);
939 // now we have an sp element with two children: the first child (we don't know nothing about it)
941 assert(cursor.parent().size() == 2);
943 // to delete the script we can invoke the do_gdelete_script(), which will do all controls we need.
944 // To give the possibility of insering an exponent
947 else if (parent.parent().isSp())
949 // in this case we have to place the cursor after the sp element
951 assert(parent.parent().parent());
952 parent.parent().parent().append(cursor);
957 // in this case the cursor is the only element of the phantom group,
958 // so we have to remove it. But, a phantom group has a special role,
959 // so we have to control the grand father of the cursor.
960 TNode gfather = parent.parent();
963 // If here, the TML tree is in an inconsistent state
964 logger.error("do_gdelete_phantom: TML tree in a inconsistent state");
966 else if (gfather.isC())
968 // in this case the phantom group is child of a MACRO.
969 // We have to control the nature of this MACRO.
970 assert(!frames.empty());
971 Frame& frame = frames.top();
973 if (frame.entry.leftOpen && frame.entry.rightOpen)
975 // in this case, the cursor'parent is in the second and last child
976 // of the MACRO. We can assert that the grand father has two
977 // children, which are both phantom group
978 assert(gfather.size() == 2);
979 assert((gfather.last() == parent) && (gfather.first().isG() && !gfather.first().hasId()));
980 assert(frame.pos == 0);
982 TNode ggfather = gfather.parent();
986 // i have to replace the gfather with the elements of its first child
987 gfather.replace(gfather.first().first(), TNode());
988 ggfather.append(cursor);
989 // now we have the situation preceding the insertion of the leftOpen and rightOpen MACRO.
990 // this MACRO no longer exists.
993 else if (frame.entry.rightOpen)
995 // the user has inserted a rightOpen MACRO, and now, this MACRO has no children (excluding the
996 // phantom group), so we remove the MACRO.
997 // We can assert that cursor's parent is the only child of the MACRO
998 assert(gfather.size() == 1);
999 assert(frame.pos == 0);
1002 gfather.replace(cursor);
1004 // now we have the situation preceding the rightOpen MACRO, so we have to pop the frame
1007 else if (frame.entry.leftOpen)
1009 // this situation will never occur.
1010 logger.error("the parser has generated a wrong TML tree");
1012 else if (!frame.entry.pattern.empty())
1014 // the MACRO accepts arguments, and the phantom group in which
1015 // the cursor is, rappresents a delimited argument
1016 // We have to control if the cursor's parent has a preceding element,
1018 TNode uncle = parent.prev();
1021 // the parent is the only element of the MACRO.
1022 // we can assert that frame.pos == 0.
1023 // In this case we can replace the MACRO with the cursor
1024 assert(frame.pos == 0);
1027 gfather.replace(cursor);
1032 // the parent has a preceding element. Now we have
1033 // to control if the uncle is a delimited argument or not.
1034 if (uncle.isG() && !uncle.hasId())
1036 // the uncle is a delimited argument. So we have to ideally
1037 // remove the sequence of delimiters.
1038 assert(frame.pos > 1);
1039 unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
1040 assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
1041 assert(sequence_length);
1042 // sequence_length is the length of the delimiters sequence which separates
1043 // the current parameter and the previous parameter
1044 frame.pos = frame.pos - sequence_length - 1;
1047 uncle.append(cursor);
1051 // the uncle is a not delimited argument, so we try to remove it.
1053 parent.replace(cursor);
1054 parent = cursor.parent(); // i update the parent (it should be the MACRO)
1055 assert(parent.isC());
1057 // now we try to remove the uncle (now it' the preceding element)
1060 } // this is the else's end, that handles the case in which an uncle exists
1061 } // end of if (!frame.entry.pattern.empty())
1064 // the entry has no arguments, is not rightOpen and is not leftOpen.
1065 logger.warning("parser: TML tree in a strange state, but we try to recover from it");
1066 if (gfather.size() == 1)
1070 gfather.replace(cursor);
1074 logger.warning("parser: TML tree in a very strange state, but we try to recover from it");
1076 gfather.replace(cursor);
1079 } // end of if (gfather.isC())
1080 else if (gfather.is("cell"))
1082 // A table is a control sequence, so there is a frame in the stack
1083 assert(!frames.empty());
1084 assert(frames.top().pos == 1);
1085 assert(frames.top().entry.table == 1);
1087 // a cell MUST be a row's child, which in turn is a table's child
1088 assert(gfather.parent() && gfather.parent().is("row") && gfather.parent().parent());
1089 TNode row = gfather.parent();
1091 // in this case the cell has no element, so the user wants to delete this cell.
1092 TNode prev_cell = gfather.prev();
1096 // now the cell no longer exists
1100 // in this case, the cell is the only cell in the row.
1101 // So, we assume that the user wants to delete the entire row.
1102 TNode table = row.parent();
1103 TNode prev_row = row.prev();
1108 // the row was the only child of the table.
1109 // so we have to delete the entire table
1110 assert(table.parent());
1111 TNode parent_table = table.parent();
1114 parent_table.append(cursor);
1118 // there are other rows (one or more)
1119 assert(prev_row.is("row"));
1120 assert(prev_row.last());
1121 TNode last_cell = prev_row.last();
1122 assert(last_cell.is("cell"));
1123 assert(last_cell.size() == 1);
1124 assert(last_cell.first().isG() && !last_cell.first().hasId());
1125 last_cell.first().append(cursor);
1127 } // end of if (!prev_cell)
1130 // being here means that there is a previous cell,
1131 // so we append the cursor to group.
1132 assert(prev_cell.size() == 1);
1133 assert(prev_cell.first().isG() && !prev_cell.first().hasId());
1134 prev_cell.first().append(cursor);
1136 } // end of if (gfather.is("cell"))
1137 else if (gfather.isSp())
1139 // in this case, the user typed a \'. So this phantom group
1140 // contained a sequence of \'.
1141 // Maybe in this part will never be used, because, if we delete last \' in the
1142 // phantom group, we remove the phantom group also
1144 // In any case, if we are here we have two possibilities:
1145 // we can delete the phantom group;
1146 // we can delete the superscript.
1147 // At the moment we implement the first solution. To implement the second one, just remove
1148 // delete the line code after the logger.warning and remove comment from the remaining lines
1149 logger.warning("parser: TML tree in a strange state, we try to recover from it");
1150 parent.replace(cursor);
1154 //gfather.replace(cursor);
1156 else if (gfather.is("math"))
1158 // in this case we ignore the user's will of deleting
1159 // but we can decide to remove the math mode.
1160 logger.info("Parser: nothing to delete");
1164 // cursor' grand father is undefined
1165 logger.error("parser: TML tree is in a unknown state");
1167 } // end of the else of the if (prev)
1173 TPushParser::do_gdelete()
1175 // we have to handle the case in wich the cursor has a parent or not
1176 if (!cursor.parent())
1178 // it's not a good situation...at the moment we do not take actions
1179 logger.error("TML tree not well structured");
1183 // a parent exists. We have to take differnt actions, based on the nature of
1185 TNode parent = cursor.parent();
1186 if (parent.is("math"))
1188 // we ca do two thing...we can remove the math mode (it implies controlling the display attribute), we can do nothing
1189 // At the moment, the user's will of deleting is simply ignored
1190 logger.error("TML tree not well structured");
1192 else if (parent.isG())
1194 // the cursor's parent is a group. We have to control if it's a phantom group or not
1197 do_gdelete_groupId();
1201 do_gdelete_phantom_group();
1203 } // end of parent is group
1204 else if (parent.isC())
1207 } // end of parent is a MACRO
1208 else if (parent.isSp() || parent.isSb())
1210 do_gdelete_script();
1211 } // end of parent is sp or sb
1212 } // end of the else which consider the case in which parent exists
1214 } // end of method do_gdelete
1217 TPushParser::process(const TToken& token)
1219 switch (token.category)
1221 case TToken::BEGIN: do_begin(); break;
1222 case TToken::END: do_end(); break;
1223 case TToken::SHIFT: do_shift(); break;
1224 case TToken::ALIGN: do_align(); break;
1225 case TToken::EOL: do_eol(); break;
1226 case TToken::PARAMETER: do_parameter(token.value); break;
1227 case TToken::SUPERSCRIPT: do_superscript(); break;
1228 case TToken::SUBSCRIPT: do_subscript(); break;
1229 case TToken::SPACE: do_space(token.value); break;
1230 case TToken::LETTER: do_letter(token.value); break;
1231 case TToken::DIGIT: do_digit(token.value); break;
1232 case TToken::OTHER: do_other(token.value); break;
1233 case TToken::ACTIVE: do_active(token.value); break;
1234 case TToken::COMMENT: do_comment(); break;
1235 case TToken::CONTROL: do_control(token.value); break;
1236 case TToken::GDELETE: do_gdelete(); break;
1241 TPushParser::push(const TToken& token)
1243 if (token.category == TToken::GDELETE)
1250 TNode parent = cursor.parent();
1251 // If the cursor has no parent then it is detached from the editing
1252 // tree, which means this token will be ignored
1255 // If the parent is a phantom group and the grand-parent is a
1256 // control sequence, there are two cases:
1257 // a. we are parsing a delimited argument of a entry
1258 // b. we are parsing a side of a right- or left-open entry
1259 if (parent.isG() && !parent.hasId() && parent.parent().isC())
1261 // There must be an open frame, for the grand-parent is a control sequence
1262 assert(!frames.empty());
1263 Frame& frame = frames.top();
1264 if (!frame.entry.pattern.empty())
1266 // The entry pattern is not empty. By our conventions this means
1267 // the entry cannot be open at either end, hence we are parsing
1268 // a delimited argument
1269 assert(frame.pos + 1 < frame.entry.pattern.size());
1270 assert(frame.entry.pattern[frame.pos + 1].category != TToken::PARAMETER);
1271 if (frame.entry.pattern[frame.pos + 1] == token)
1273 // The token matches with a delimiter of the argument,
1274 // so we increment the frame.pos
1277 if (frame.entry.lastDelimiter(frame.pos))
1279 // this delimiter is the last one for the argumet,
1280 // so the argument is completed
1284 if (frame.pos == frame.entry.pattern.size())
1286 // This token has completed the entry
1289 else if (frame.entry.paramDelimited(frame.pos))
1291 // For the next is a delimited argument we have to place
1292 // a suitable phantom group with the cursor inside
1293 TNode g = doc.createG();
1294 parent.parent().append(g);
1298 parent.parent().append(cursor);
1303 // Delimiter mismatch.
1304 if (frame.entry.pattern[frame.pos].category != TToken::PARAMETER)
1306 // in this case, there is a sequence of delimiters that delimitates
1307 // the argument, and the user correctly inserted a portion of this
1308 // sequence, but now has inserted a wrong delimiter.
1309 // Here, there are some possibilities:
1310 // - ignore the token, and wait for the correct delimiter
1311 // - ignore the token, wait for the correct delimiter and emit an error
1312 // At the moment, we implement the second possibily
1313 logger.error("parser: it's not the correct delimiter...you have to type " + frame.entry.pattern[frame.pos + 1].value);
1317 // in this case, the sequence of delimiters is composed of one
1318 // delimiter. It means that we have to process the token
1325 // The entry pattern is empty, hence we are parsing a right-open
1326 // entry. What happens if we actually are in the left side?
1327 // This could happen only when re-editing an entered expression
1329 assert(frame.entry.rightOpen);
1333 else if (parent.isC())
1335 // We are parsing a non-delimited argument entry
1337 Frame& frame = frames.top();
1338 assert(frame.pos < frame.entry.pattern.size());
1340 if (frame.entry.pattern[frame.pos].category == TToken::PARAMETER)
1342 // As by the TeX parsing rules of undelimited parameters,
1343 // empty spaces are ignored
1344 if (token.category != TToken::SPACE)
1346 // We need to increase the frame position here, becase inside
1347 // process the function advance will be called. At that point
1348 // it will be important for the parser to know that the entry
1349 // has been completed in order to place the cursor correctly
1350 // in the next position
1355 else if (frame.entry.pattern[frame.pos] == token)
1357 // The token has been accepted
1359 if (frame.pos < frame.entry.pattern.size() &&
1360 frame.entry.paramDelimited(frame.pos))
1362 // If the next is a delimited argument we have to place
1363 // the phantom group with the cursor inside
1364 TNode g = doc.createG();
1373 // There is a mismatch. Emit an error and ignore the token?
1374 logger.debug("parser: token ignored: " + token.value);
1382 logger.warning("ignored token");
1385 } // this end corresponds to the else of the if (token.category == TToken::GDELETE)
1387 if (factory) factory->documentModified(doc);
1392 TPushParser::advance(const TNode& node)
1395 TNode parent = node.parent();
1397 ; // nothing to do, the cursor is not in the document any more
1398 else if (parent.isG())
1400 TNode next = node.next();
1401 if (next) next.insert(cursor);
1402 else parent.append(cursor);
1404 else if (parent.isC())
1410 Frame& frame = frames.top();
1411 if (frame.pos == frame.entry.pattern.size())
1417 parent.append(cursor);
1420 else if (parent.is("math"))
1427 TPushParser::setCursorHint(const std::string& c)
1430 if (factory) factory->documentModified(doc);