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)
16 TPushParser::~TPushParser()
23 cursor = doc.create("cursor");
24 cursor["visible"] = "1";
32 cout << "passo di qui dentro pushparser " << factory << endl;
34 if (cursor.parent()) cursor.remove();
36 doc.root().append(cursor);
37 if (factory) factory->documentModified(doc);
41 TPushParser::PRIME() const
43 const TDictionary::Entry entry = dictionary.find("prime");
44 if (entry.cls == TDictionary::OPERATOR) return entry.value;
49 TPushParser::do_begin()
51 TNode parent = cursor.parent();
52 if (parent.isC() && dictionary.find(parent.nameC()).table)
54 TNode row = doc.create("row");
55 TNode cell = doc.create("cell");
56 TNode g = doc.createG();
64 TNode g = doc.createG(nextId++);
71 TPushParser::correctBrace()
73 // this method MUST be invoked when the cursor is child of a
74 // phantom group, which in turn is the last rightOpen MACRO's child.
75 // The only way to exit from a rightOpen MACRO is opening a group before
76 // inserting the MACRO and, once the MACRO is completely inserted, closing
78 // This method return true if the condition above is true. False, otherwise.
79 assert(cursor.parent() && cursor.parent().isG() && !cursor.parent().hasId());
80 TNode parent = cursor.parent();
81 assert(parent.parent() && parent.parent().isC());
82 assert(!frames.empty());
83 Frame& frame = frames.top();
84 assert(frame.entry.rightOpen);
85 assert(parent.parent().last() == parent);
87 TNode c = parent.parent();
90 TNode node = c.parent();
93 if (node.isG() && node.hasId())
95 // in this case, the rightOpen MACRO is a child of a group with id.
96 // So, the '}' is correct
102 // the MACRO is a phantom group's child. We have to control why we
103 // have this phantom group
104 TNode nodeParent = node.parent();
105 if (nodeParent && nodeParent.isC())
107 // we have to control the nature of this MACRO
108 const TDictionary::Entry& entry = dictionary.find(nodeParent.nameC());
109 if (entry.rightOpen && node == nodeParent.last())
111 // in this case we have to re-iterate the process
112 node = nodeParent.parent();
120 // at the moment we assume that a MACRO cannot be child of an element other than a group
130 TPushParser::do_end()
132 TNode parent = cursor.parent();
133 if (parent && parent.isG() && parent.hasId())
135 // normal closing brace for an explicitly open group
139 else if (parent && parent.isG() && parent.parent() && parent.parent().is("cell"))
141 assert(!frames.empty());
142 // closing brace for a structure in which & or \cr have been used
143 TNode row = parent.parent().parent();
144 assert(row && row.is("row"));
145 assert(row.parent());
148 else if (parent && parent.isG() && !parent.hasId() && parent.parent() && !parent.parent().is("math"))
150 // In this case, we have to control the cursor's grand parent.
151 TNode gparent = parent.parent();
153 if (gparent.isC() && gparent.last() == parent)
155 // a frame MUST be in the stack
156 assert(!frames.empty());
158 // we have to control the nature of this macro
159 if (frames.top().entry.rightOpen)
161 // in this case, the '}' character is the proper way to exit from the phantom group, and
162 // in particular, this character means that the user wants to exit from the MACRO.
163 // A rightOpen MACRO MUST be descendant of a group with Id. This '}' is the closing brace of this
164 // group. So, we have to control if this group exists. This groyp could exist, but this MACRO could
165 // be another MACRO's child, so we have to control this last MACRO recursively. This recurive control
166 // is done by the correctBrace method.
169 // the '}' is not correct
170 logger.warning("nothing to close");
181 logger.error("closing brace ignored");
186 // at the moment, a phantom group with cursor can be a MACRO's child or a cell's child, and these cases
187 // are handled in other blocks of code.
188 logger.error("do_end: strange TML tree");
193 // In this case, there is a redundant '}', so we can ignore it and
195 logger.warning("There is so no corresponding'{'");
203 TPushParser::do_end()
205 TNode parent = cursor.parent();
206 if (parent && parent.isG() && parent.hasId())
208 // normal closing brace for an explicitly open group
212 else if (parent && parent.isG() && parent.parent() && parent.parent().is("cell"))
214 assert(!frames.empty());
215 // closing brace for a structure in which & or \cr have been used
216 TNode row = parent.parent().parent();
217 assert(row && row.is("row"));
218 TNode table = row.parent();
222 else if (parent && parent.isG() && !parent.hasId() && parent.parent() && !parent.parent().is("math"))
224 // closing brace for a right-open macro (like \over)
225 // It's not sure that we want to exit from a phantom group with a
226 // '}', because, to enter in the phantom group the user didn't insert a
233 // In this case, there is a redundant '}', so I think we can ignore it and
235 logger.warning("There is so no corresponding'{'");
242 TPushParser::do_shift()
244 TNode parent = cursor.parent();
246 if (parent.is("tex"))
248 TNode math = doc.create("math", nextId++);
249 TNode g = doc.createG();
250 cursor.replace(math);
254 else if (parent.isG() && !parent.hasId() && parent.parent() && parent.parent().is("math"))
258 // there is something before the cursor, hence this is the
259 // closing math shift
260 if (parent.parent()["display"] != "1")
262 // one math shift is enough to close it
267 // we need two closing math shifts
268 //cursor.remove(); ??
269 parent.parent().append(cursor);
272 else if (parent.parent()["display"] != "1")
274 // there is nothing before the cursor, and the math is not
275 // in display mode, so this must be a double math shift
276 parent.parent()["display"] = "1";
280 parent.parent().append(cursor);
283 else if (parent.is("math"))
289 logger.error("parser: math shift");
294 TPushParser::do_align()
296 TNode parent = cursor.parent();
297 if (parent && parent.isG() && parent.hasId())
299 // alignment tab used for the first time inside a group
300 TNode row = doc.create("row");
301 TNode cell = doc.create("cell");
302 TNode g = doc.createG();
305 g.append(parent.first(), cursor);
307 else if (parent && parent.isG() && parent.parent().is("cell"))
309 // alignment tab used within a cell
310 TNode oldCell = parent.parent();
311 assert(oldCell && oldCell.is("cell"));
312 TNode row = oldCell.parent();
313 assert(row && row.is("row"));
314 TNode cell = doc.create("cell");
315 if (oldCell.next()) oldCell.next().insert(cell);
316 else row.append(cell);
317 TNode g = doc.createG();
323 logger.error("alignment tab used outside matrix");
328 TPushParser::do_eol()
330 //if (cursor.parent()) cursor.remove();
334 TPushParser::do_parameter(const std::string& p)
340 TPushParser::do_subscript()
342 TNode parent = cursor.parent();
345 TNode prev = cursor.prev();
348 TNode elem = doc.create("sb", nextId++);
349 TNode g = doc.createG();
350 cursor.replace(elem);
356 TNode elem = doc.create("sb", nextId++);
362 else if (parent.isSb() && cursor == parent[1])
364 if (parent["under"] == "1") logger.error("already under");
365 else parent["under"] = "1";
370 TPushParser::do_superscript()
372 TNode parent = cursor.parent();
375 TNode prev = cursor.prev();
378 TNode elem = doc.create("sp", nextId++);
379 TNode g = doc.createG();
380 cursor.replace(elem);
386 TNode elem = doc.create("sp", nextId++);
392 else if (parent.isSp() && cursor == parent[1])
394 if (parent["over"] == "1") logger.error("already over");
395 else parent["over"] = "1";
400 TPushParser::do_space(const std::string&)
402 logger.debug("do_space");
406 TPushParser::do_letter(const std::string& s)
408 //TNode parent = cursor.parent();
409 TNode elem = doc.createI(s, nextId++);
410 cursor.replace(elem);
415 TPushParser::do_digit(const std::string& s)
417 TNode parent = cursor.parent();
418 TNode prev = cursor.prev();
419 if (prev && parent.isG() && prev.is("n"))
421 TNode elem = doc.createN(prev.value() + s, nextId++);
426 TNode elem = doc.createN(s, nextId++);
427 cursor.replace(elem);
433 TPushParser::isPrimes(const TNode& node) const
436 return node.isG() && node.last() && node.last().is("o") && node.last()["val"] == PRIME();
440 TPushParser::do_apostrophe()
442 if (cursor.parent() && cursor.parent().isG())
444 if (TNode prev = cursor.prev())
446 if (prev.isSp() && prev[1] && isPrimes(prev[1]))
447 prev[1].append(doc.createO(PRIME(), nextId++));
448 else if (prev.isSb() && prev[0] &&
449 prev[0].isSp() && prev[0][1] &&
450 isPrimes(prev[0][1]))
451 prev[0][1].append(doc.createO(PRIME(), nextId++));
454 TNode elem = doc.create("sp");
455 TNode g = doc.createG();
459 g.append(doc.createO(PRIME(), nextId++));
465 logger.error("parser: you have to type one identifier before the ''");
475 TPushParser::do_other(const std::string& s)
483 /*cout << "TPushParser::do_other " << s << endl;
484 cout << "DOCUMENT: " << static_cast<GdomeNode*>(cursor.element().get_ownerDocument()) << endl;*/
485 TNode elem = doc.createT("o", s, nextId++);
486 cursor.replace(elem);
493 TPushParser::do_active(const std::string&)
499 TPushParser::do_comment()
507 TNode parent = cursor.parent();
508 if (parent && parent.isG() &&
509 parent.parent() && parent.parent().is("cell") &&
510 parent.parent().parent() && parent.parent().parent().is("row"))
512 TNode oldRow = parent.parent().parent();
514 TNode table = oldRow.parent();
516 TNode row = doc.create("row");
517 TNode cell = doc.create("cell");
518 TNode g = doc.createG();
519 if (oldRow.next()) oldRow.next().insert(row);
520 else table.append(row);
528 TPushParser::do_control(const std::string& name)
530 if (name == "cr") do_cr();
533 TNode parent = cursor.parent();
534 const TDictionary::Entry& entry = dictionary.find(name);
537 case TDictionary::IDENTIFIER:
539 TNode t = doc.createI(entry.value, nextId++);
545 case TDictionary::OPERATOR:
547 TNode t = doc.createO(entry.value, nextId++);
553 case TDictionary::NUMBER:
555 TNode t = doc.createN(entry.value, nextId++);
561 case TDictionary::MACRO:
563 TNode m = doc.createC(name, nextId++);
566 if (entry.leftOpen && entry.rightOpen)
568 assert(entry.pattern.empty());
569 assert(parent.isG());
570 TNode g1 = doc.createG();
571 g1["left-open"] = "1";
572 g1.append(parent.first(), m);
574 TNode g2 = doc.createG();
577 frames.push(Frame(entry));
579 else if (entry.leftOpen)
581 assert(parent.isG());
582 TNode g = doc.createG();
583 g["left-open"] = "1";
584 g.append(parent.first(), m);
588 else if (entry.rightOpen)
590 assert(entry.pattern.empty());
591 assert(parent.isG());
592 TNode g = doc.createG();
595 frames.push(Frame(entry));
597 else if (!entry.pattern.empty())
601 frames.push(Frame(entry));
603 if (entry.paramDelimited(0))
605 TNode g = doc.createG();
614 // error, but we could handle this very easily
615 logger.error(" parser:but we could handle this easily");
621 case TDictionary::UNDEFINED:
623 logger.error("parser: using undefined macro " + name);
624 TNode m = doc.createC(name, nextId++);
636 TPushParser::gdelete_prev_token()
638 assert(cursor.prev());
639 assert(cursor.parent());
640 TNode prev = cursor.prev();
641 assert(prev.is("i") || prev.is("o") || prev.is("n"));
643 // the control below is designed to handle the case in which val have more than one unicode character
644 DOM::UCS4String ucs4val(prev.element().getAttribute("val"));
645 if ((prev.is("i")) || (ucs4val.length() <= 1) || prev.element().hasAttribute("name"))
648 prev.replace(cursor);
650 if (cursor.parent().isC())
652 // in this case we have removed an element of a MACRO.
653 // we can assert that this element was a non delimited argument
654 assert(!frames.empty());
655 Frame& frame = frames.top();
656 assert(frame.pos > 0);
663 ucs4val.erase(ucs4val.length() - 1, 1);
664 prev.element().setAttribute(DOM::GdomeString("val"), DOM::GdomeString(ucs4val));
670 TPushParser::gdelete_prev_script()
672 // this method deletes an sp or an sb preceding the cursor
673 assert(cursor.prev());
674 assert(cursor.parent());
675 TNode prev = cursor.prev();
676 assert(prev.is("sp") || prev.is("sb"));
679 // we can invoke the gdelete_prev, because a sp (sb) MUST have two children
684 TPushParser::gdelete_prev_group()
686 assert(cursor.prev() && cursor.prev().isG());
687 TNode prev = cursor.prev();
691 // a group could have no children, so the gdelete_prev is not appropriate
692 // so, this method is not equivalent to the one above
697 TPushParser::gdelete_prev_macro()
699 assert(cursor.parent());
700 assert(cursor.prev());
701 TNode prev = cursor.prev();
704 const TDictionary::Entry& entry = dictionary.find(prev["name"]);
706 if (!entry.defined())
708 // We can assume that the user want to completely delete the undefined macro
710 prev.replace(cursor);
714 // we start to remove a MACRO. Different actions must be taken, based on the nature
715 // of the MACRO. In some cases, we can't remove the MACRO immediately, in other
716 // cases it's correct. In the first set of cases, we have to update the stack, pushing
717 // a frame in it with a correct value of pos, in the
718 // second one, we must not push a frame in the stack
722 // In this fragment of code we also handle the leftOpen && rightOpen MACRO.
723 // if the control element is rightOpen, the cursor should be placed after
724 // the last child of the control element's last child, and than, we try to remove something.
725 // A frame MUST be pushed in the stack, because we dont' know if the following actions
726 // will completely remove the MACRO.
727 frames.push(Frame(entry));
729 // Since the MACRO is rightOpen, the last child of the MACRO must be a phantom group
730 assert(prev.last().isG() && !prev.last().hasId());
733 prev.last().append(cursor);
735 // the gdelete_prev is not appropriate, because the last child of the MACRO could have no children
736 do_gdelete_phantom_group();
738 else if (entry.leftOpen)
740 // the leftOpen MACRO MUST have one and only one child, which MUST be a phantom group
741 // In this case, we do not have to push a frame in the stack, because we remove the
742 // MACRO immediately, substituting it with the content of the phantom group.
743 // At the moment, we don't remove the last child of the phantom group, but
744 // it's not clear if it's the correct behavior of the graphical deleting.
745 // To delete it, just remove the comment of the last instruction of the
746 // if (g.size()) block.
747 assert(prev.first());
748 assert(prev.first().isG());
749 assert(prev.first() == prev.last());
751 TNode g = prev.first();
754 // in this case, the phantom group has at least one child, so we can call the
757 prev.replace(g.first(), TNode());
762 // otherwise, the phantom group has no children, so we remove it, also the MACRO.
765 prev.replace(cursor);
768 else if (!entry.pattern.empty())
770 // we have to start to remove a MACRO which accepts arguments.
771 // If the MACRO accepts arguments, the MACRO has at least one child
772 assert(prev.size() >= 1);
774 // Differnt actions must be taken, based on the nature of the last child
775 // of the MACRO. We have to distinguish the case in which it's a delimited argument,
776 // frome the one in which it's a not delimited argument.
777 if (prev.last().isG() && !prev.last().hasId())
779 // the last argument of the MACRO is a delimited argumet. We ideally remove
780 // the sequence of delimiters
782 prev.last().append(cursor);
783 // we have to push a frame with a correct value of pos
784 assert(entry.previousParam(entry.pattern.size()) != entry.pattern.size());
786 unsigned sequence_length = entry.pattern.size() - entry.previousParam(entry.pattern.size()) - 1;
787 unsigned p = entry.pattern.size() - sequence_length - 1;
788 // now, p is the correct value of pos, and we can push the frame.
789 frames.push(Frame(entry, p));
793 // in this case, the last child of the MACRO is not a delimited argument, so we try
794 // to remove it, but we have to take differnt actions if the MACRO is a table with rows or not.
796 if (entry.table == 1 && prev.last().is("row"))
798 // in this case the cursor should be appended to the group associated to
799 // the last cell of the last row of the table
800 assert(prev.last().last().is("cell") && prev.last().last().first().isG());
801 prev.last().last().first().append(cursor);
808 // we push a frame in the stack with a correct value of member pos.
809 // This correct value is the size of the pattern, because we have to START to delete
810 // a MACRO. It means that all of the MACRO's arguments have been inserted.
811 frames.push(Frame(entry, entry.pattern.size()));
815 // in this case we try to remove the last child of the MACRO.
820 // this block of code will never be executed, because the MACRO accepts arguments,
821 // so a MACRO's child MUST exist. But, we can handle this case, emitting a warning
822 logger.warning("Parser: the TML tree is in a strange state, but a good state will be generated");
825 } // end of the else of the if (prev.last().isG() && !prev.last().hasId())
827 } // end of if (!entry.pattern.empty())
830 // if we are here, the MACRO preceding the cursor, is !(rightOpen || leftOpen),
831 // and has no pattern. It means that it has no children.
832 // We can replace it with the cursor
833 assert(prev.size() == 0);
835 prev.replace(cursor);
837 } // end of defined MACRO
842 TPushParser::gdelete_prev()
844 // if in this function, the prev of cursor does exist, also the parent and we want a graphical deleting.
846 assert(cursor.prev());
847 assert(cursor.parent());
849 TNode prev = cursor.prev();
851 if (prev.is("i") || prev.is("o") || prev.is("n"))
853 gdelete_prev_token();
855 else if (prev.isSp() || prev.isSb())
857 gdelete_prev_script();
861 gdelete_prev_group();
865 // here, we also treat the case in which the MACRO is a table
866 gdelete_prev_macro();
870 // not handled. Future cases...
876 TPushParser::rgreplace_father(void)
878 // this method MUST only be invoked, when the cursor
879 // is the only child of a group with id. This function
880 // replaces the group with the cursor. But if the new parent
881 // is a group with id and the cursor is the only child of the
882 // group, the new parent is replaced...and so on.
883 // r stands for recursive, g stands for graphical.
884 assert(cursor.parent());
885 assert(cursor.parent().isG() && cursor.parent().hasId());
887 TNode parent = cursor.parent();
889 while (parent.isG() && parent.hasId() && (parent.first() == cursor))
891 parent.replace(cursor);
892 parent = cursor.parent();
897 // in this case we have removed a MACRO's child.
898 // So, we have to update the member pos of the frame in the stack
899 // We can assert that this MACRO accepts arguments.
900 assert(!frames.empty());
901 Frame& frame = frames.top();
902 assert(frame.pos > 0);
908 TPushParser::do_gdelete_script()
910 // If we are here, the cursor is child of a script (sp or sb) and
911 // this means that a prev does exist and that there is one and only one
912 // element preceding the cursor. The sp's (or sb's) parent
913 // MUST NOT be a MACRO.
914 // The element preceding the cursor is the base of the script.
916 assert(cursor.parent() && (cursor.parent().isSp() || cursor.parent().isSb()));
917 TNode parent = cursor.parent();
919 assert(parent.size() == 2);
920 assert(parent.parent() && !parent.parent().isC());
922 TNode prev = cursor.prev();
924 if (prev.isG() /*&& !prev.hasId()*/ && (prev.size() == 0))
926 // in this case, the script's base is a group with no elements, so
927 // we have to remove the entire MACRO, replacing it with the cursor.
928 // This situation occurs when the user type something like this
935 parent.replace(cursor);
937 // if the new parent is a group with Id and the cursor is the only
938 // element of this group, we have to remove the group. These controls are made
939 // in the method rgreplace_father().
940 if (cursor.parent().isG() && cursor.parent().hasId()) rgreplace_father();
944 // in this case, the prev has to replace the script,
945 // and the cursor has to be placed after the prev.
946 assert(prev.hasId());
947 parent.replace(prev);
948 prev.parent().append(cursor);
949 // now prev have a preceding element
950 assert(cursor.parent().size() > 1);
953 } // end of method do_gdelete_script
956 TPushParser::do_gdelete_macro()
958 // If we are here, the cursor is a child of a MACRO and this means
959 // that there is an open frame for the control element
960 // and this element is closed at either side (no leftOpen no rightOpen)
961 // and the MACRO is waiting for a not delimited argument, so
962 // we can assert that frame.entry.pattern.size() >= 1
963 assert(cursor.parent() && cursor.parent().isC());
964 TNode parent = cursor.parent();
966 assert(!frames.empty());
967 Frame& frame = frames.top();
968 assert(frame.entry.pattern.size() >= 1);
970 // we have to take different actions, based on if a preceding element exists
972 TNode prev = cursor.prev();
975 // in this case, a prev does not exist, so the actions of deleting means
976 // that we have to remove the MACRO. So we have to pop the stack.
977 // Being here also means that the MACRO is waiting for the first argument
978 // (which is not delimited), but we don't mind about it.
979 assert(frame.pos == 0);
980 parent.replace(cursor);
983 // if the new parent is a group with Id, and has no elements other than the
984 // cursor, we can remove it, because it' a graphical deleting
985 if (cursor.parent() && cursor.parent().isG() && cursor.parent().hasId())
987 else if (cursor.parent().isC())
989 // We have assumed that a MACRO cannot be a MACRO's child.
990 // At the moment, this assumption is valid, but in a future
991 // it might be false.
992 assert(!frames.empty());
993 Frame& frame = frames.top();
994 assert(frame.pos > 0);
1000 // a prev does exist, we have to control if it's a delimited argument or not.
1001 if (prev.isG() && !prev.hasId())
1003 // in this case, prev is a delimited argument, so we have
1004 // to ideally remove the sequence of delimiters
1005 Frame& frame = frames.top();
1006 assert(frame.pos > 1);
1008 prev.append(cursor);
1009 assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
1011 // these 3 lines of code update the member pos.
1012 unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
1013 assert(sequence_length);
1014 frame.pos = frame.pos - sequence_length - 1;
1018 // the prev is not a delimited argument, so we have to try to remove it.
1019 // We "try", because the prev might be a something that
1020 // a simple delete cannot remove completely
1028 TPushParser::do_gdelete_groupId()
1030 // if we are here, the cursor's parent is a group with Id
1031 assert(cursor.parent() && cursor.parent().isG() && cursor.parent().hasId());
1032 TNode parent = cursor.parent();
1034 // we have to take different actions based on if the cursor has a preceding
1036 TNode prev = cursor.prev();
1039 // the cursor has a preceding element, so we try to remove it
1042 // We control if the group has to be removed, because the cursor
1043 // might be the only element of the group.
1044 if ((parent.first() == cursor) && parent.isG() && parent.hasId())
1050 // the cursor has no preceding elements, so we have to remove the
1054 // we have to re-start the process, because it' a graphical delete
1058 } // end of method do_gdelete_groupId()
1061 TPushParser::do_gdelete_phantom_group()
1063 // if we are here, the cursor MUST be a child of a
1065 assert(cursor.parent() && cursor.parent().isG() && !cursor.parent().hasId());
1067 TNode parent = cursor.parent();
1069 // now we have to control if the cursor has a preceding element or not
1070 TNode prev = cursor.prev();
1073 // the cursor has a preceding element, so we try to remove it
1076 if (parent.size() == 1 && parent.parent().isSp())
1078 // in this case the gdelete_prev has removed the only element preceding the cursor.
1079 // If the phantom group is an sp's child, it means that the user has removed all \' in the
1081 // We can remove the phamtom group and the sp element. But we also can only remove the
1082 // phantom group, giving the user the possibility of inserting an exponent.
1083 // At the moment, we remove the sp element (and the phantom group), because it may be the correct
1084 // behavior of a graphical deleting.
1086 parent.replace(cursor);
1087 // now we have an sp element with two children: the first child (we don't know anything about it)
1089 assert(cursor.parent().size() == 2);
1091 // to delete the script we can invoke the do_gdelete_script(), which will do all controls we need.
1092 // To give the possibility of insetring an exponent, just remove the following istruction.
1093 do_gdelete_script();
1095 else if (parent.parent().isSp())
1097 // in this case we have to place the cursor after the sp element
1099 assert(parent.parent().parent());
1100 parent.parent().parent().append(cursor);
1105 // in this case the cursor is the only element of the phantom group,
1106 // so we have to remove it. But, a phantom group has a special role,
1107 // so we have to control the grand father of the cursor.
1108 TNode gfather = parent.parent();
1111 // If here, the TML tree is in an inconsistent state
1112 logger.error("do_gdelete_phantom: TML tree in a inconsistent state");
1114 else if (gfather.isC())
1116 // in this case the phantom group is child of a MACRO.
1117 // We have to control the nature of this MACRO.
1118 assert(!frames.empty());
1119 Frame& frame = frames.top();
1121 if (frame.entry.leftOpen && frame.entry.rightOpen)
1123 // in this case, the cursor'parent is in the second and last child
1124 // of the MACRO. We can assert that the grand father has two
1125 // children, which are both phantom groups
1126 assert(gfather.size() == 2);
1127 assert((gfather.last() == parent) && (gfather.first().isG() && !gfather.first().hasId()));
1128 assert(frame.pos == 0);
1130 TNode ggfather = gfather.parent();
1134 // we have to replace the gfather with the elements of its first child, but this group may have no
1136 if (gfather.first().size())
1138 gfather.replace(gfather.first().first(), TNode());
1139 ggfather.append(cursor);
1143 // in this case, the MACRO has to be replaced with the cursor
1144 gfather.first().remove();
1145 gfather.replace(cursor);
1147 // now we have the situation preceding the insertion of the leftOpen and rightOpen MACRO.
1148 // this MACRO no longer exists.
1151 else if (frame.entry.rightOpen)
1153 // the user has inserted a rightOpen MACRO, and now, this MACRO has no children (excluding the
1154 // phantom group), so we remove the MACRO.
1155 // We can assert that cursor's parent is the only child of the MACRO
1156 assert(gfather.size() == 1);
1157 assert(frame.pos == 0);
1160 gfather.replace(cursor);
1162 // now we have the situation preceding the rightOpen MACRO, so we have to pop the frame
1165 else if (frame.entry.leftOpen)
1167 // this situation will never occur.
1168 logger.error("the parser has generated a wrong TML tree");
1170 else if (!frame.entry.pattern.empty())
1172 // the MACRO accepts arguments, and the phantom group in which
1173 // the cursor is, rappresents a delimited argument
1174 // We have to control if the cursor's parent has a preceding element,
1176 TNode uncle = parent.prev();
1179 // the parent is the only element of the MACRO.
1180 // we can assert that frame.pos == 0.
1181 // In this case we can replace the MACRO with the cursor
1182 assert(frame.pos == 0);
1185 gfather.replace(cursor);
1190 // the parent has a preceding element. Now we have
1191 // to control if the uncle is a delimited argument or not.
1192 if (uncle.isG() && !uncle.hasId())
1194 // the uncle is a delimited argument. So we have to ideally
1195 // remove the sequence of delimiters.
1196 assert(frame.pos > 1);
1197 unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
1198 assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
1199 assert(sequence_length);
1200 // sequence_length is the length of the delimiters sequence which separates
1201 // the current parameter and the previous parameter
1202 frame.pos = frame.pos - sequence_length - 1;
1205 uncle.append(cursor);
1209 // the uncle is a not delimited argument, so we try to remove it.
1211 parent.replace(cursor);
1212 parent = cursor.parent(); // i update the parent (it should be the MACRO)
1213 assert(parent.isC());
1215 // now we try to remove the uncle (now it' the preceding element)
1218 } // this is the else's end, that handles the case in which an uncle exists
1219 } // end of if (!frame.entry.pattern.empty())
1222 // the entry has no arguments, is not rightOpen and is not leftOpen.
1223 logger.warning("parser: TML tree in a strange state, but we try to recover from it");
1224 if (gfather.size() == 1)
1228 gfather.replace(cursor);
1232 logger.warning("parser: TML tree in a very strange state, but we try to recover from it");
1234 gfather.replace(cursor);
1237 } // end of if (gfather.isC())
1238 else if (gfather.is("cell"))
1240 // A table is a control sequence, so there is a frame in the stack
1241 assert(!frames.empty());
1242 assert(frames.top().pos == 1);
1243 assert(frames.top().entry.table == 1);
1245 // a cell MUST be a row's child, which in turn is a table's child
1246 assert(gfather.parent() && gfather.parent().is("row") && gfather.parent().parent());
1247 TNode row = gfather.parent();
1249 // in this case the cell has no element, so the user wants to delete this cell.
1250 TNode prev_cell = gfather.prev();
1254 // now the cell no longer exists
1258 // in this case, the cell is the only cell in the row.
1259 // So, we assume that the user wants to delete the entire row.
1260 TNode table = row.parent();
1261 TNode prev_row = row.prev();
1266 // the row was the only child of the table.
1267 // so we have to delete the entire table
1268 assert(table.parent());
1269 TNode parent_table = table.parent();
1272 parent_table.append(cursor);
1276 // there are other rows (one or more)
1277 assert(prev_row.is("row"));
1278 assert(prev_row.last());
1279 TNode last_cell = prev_row.last();
1280 assert(last_cell.is("cell"));
1281 assert(last_cell.size() == 1);
1282 assert(last_cell.first().isG() && !last_cell.first().hasId());
1283 last_cell.first().append(cursor);
1285 } // end of if (!prev_cell)
1288 // being here means that there is a previous cell,
1289 // so we append the cursor to group.
1290 assert(prev_cell.size() == 1);
1291 assert(prev_cell.first().isG() && !prev_cell.first().hasId());
1292 prev_cell.first().append(cursor);
1294 } // end of if (gfather.is("cell"))
1295 else if (gfather.isSp())
1297 // in this case, the user typed a \'. So this phantom group
1298 // contained a sequence of \'.
1299 // Maybe in this part will never be used, because, if we delete last \' in the
1300 // phantom group, we remove the phantom group also
1302 // In any case, if we are here we have two possibilities:
1303 // we can delete the phantom group;
1304 // we can delete the superscript.
1305 // At the moment we implement the first solution. To implement the second one, just remove
1306 // the line code after the logger.warning and remove comments from the remaining lines
1307 logger.warning("parser: TML tree in a strange state, we try to recover from it");
1308 parent.replace(cursor);
1312 //gfather.replace(cursor);
1314 else if (gfather.is("math"))
1316 // in this case we ignore the user's will of deleting
1317 // but we can decide to remove the math mode.
1318 logger.info("Parser: nothing to delete");
1322 // cursor's grand father is undefined
1323 logger.error("parser: TML tree is in a unknown state");
1325 } // end of the else of the if (prev)
1331 TPushParser::do_gdelete()
1333 // we have to handle the case in wich the cursor has a parent or not
1334 if (!cursor.parent())
1336 // it's not a good situation...at the moment we do not take actions
1337 logger.error("TML tree not well structured");
1341 // a parent exists. We have to take differnt actions, based on the nature of
1343 TNode parent = cursor.parent();
1344 if (parent.is("math"))
1346 // we ca do two thing...we can remove the math mode (it implies controlling the display attribute), we can do nothing
1347 // At the moment, the user's will of deleting is simply ignored
1348 logger.warning("nothing to delete");
1350 else if (parent.isG())
1352 // the cursor's parent is a group. We have to control if it's a phantom group or not
1355 do_gdelete_groupId();
1359 do_gdelete_phantom_group();
1361 } // end of parent is group
1362 else if (parent.isC())
1365 } // end of parent is a MACRO
1366 else if (parent.isSp() || parent.isSb())
1368 do_gdelete_script();
1369 } // end of parent is sp or sb
1370 } // end of the else which consider the case in which parent exists
1372 } // end of method do_gdelete
1375 TPushParser::process(const TToken& token)
1377 switch (token.category)
1379 case TToken::BEGIN: do_begin(); break;
1380 case TToken::END: do_end(); break;
1381 case TToken::SHIFT: do_shift(); break;
1382 case TToken::ALIGN: do_align(); break;
1383 case TToken::EOL: do_eol(); break;
1384 case TToken::PARAMETER: do_parameter(token.value); break;
1385 case TToken::SUPERSCRIPT: do_superscript(); break;
1386 case TToken::SUBSCRIPT: do_subscript(); break;
1387 case TToken::SPACE: do_space(token.value); break;
1388 case TToken::LETTER: do_letter(token.value); break;
1389 case TToken::DIGIT: do_digit(token.value); break;
1390 case TToken::OTHER: do_other(token.value); break;
1391 case TToken::ACTIVE: do_active(token.value); break;
1392 case TToken::COMMENT: do_comment(); break;
1393 case TToken::CONTROL: do_control(token.value); break;
1398 TPushParser::push(const TToken& token)
1400 TNode parent = cursor.parent();
1401 // If the cursor has no parent then it is detached from the editing
1402 // tree, which means this token will be ignored
1405 // If the parent is a phantom group and the grand-parent is a
1406 // control sequence, there are two cases:
1407 // a. we are parsing a delimited argument of a entry
1408 // b. we are parsing a side of a right- or left-open entry
1409 if (parent.isG() && !parent.hasId() && parent.parent().isC())
1411 // There must be an open frame, for the grand-parent is a control sequence
1412 assert(!frames.empty());
1413 Frame& frame = frames.top();
1414 if (!frame.entry.pattern.empty())
1416 // The entry pattern is not empty. By our conventions this means
1417 // the entry cannot be open at either end, hence we are parsing
1418 // a delimited argument
1419 assert(frame.pos + 1 < frame.entry.pattern.size());
1420 assert(frame.entry.pattern[frame.pos + 1].category != TToken::PARAMETER);
1421 if (frame.entry.pattern[frame.pos + 1] == token)
1423 // The token matches with a delimiter of the argument,
1424 // hence we increment the frame.pos
1427 if (frame.entry.lastDelimiter(frame.pos))
1429 // this delimiter is the last one for the argumet,
1430 // so the argument is completed
1434 if (frame.pos == frame.entry.pattern.size())
1436 // This token has completed the entry
1439 else if (frame.entry.paramDelimited(frame.pos))
1441 // For the next is a delimited argument we have to place
1442 // a suitable phantom group with the cursor inside
1443 TNode g = doc.createG();
1444 parent.parent().append(g);
1448 parent.parent().append(cursor);
1453 // Delimiter mismatch.
1454 if (frame.entry.pattern[frame.pos].category != TToken::PARAMETER)
1456 // in this case, there is a sequence of delimiters that delimitates
1457 // the argument, and the user has correctly inserted a portion of this
1458 // sequence, but now has inserted a wrong delimiter.
1459 // Here, there are some possibilities:
1460 // - ignore the token, and wait for the correct delimiter
1461 // - ignore the token, wait for the correct delimiter and emit an error
1462 // At the moment, we implement the second possibily
1463 logger.error("parser: it's not the correct delimiter...you have to type " + frame.entry.pattern[frame.pos + 1].value);
1467 // in this case, the sequence of delimiters is composed of one
1468 // delimiter. It means that we have to process the token
1475 // The entry pattern is empty, hence we are parsing a right-open
1476 // entry. What happens if we actually are in the left side?
1477 // This could happen only when re-editing an entered expression
1479 assert(frame.entry.rightOpen);
1483 else if (parent.isC())
1485 // We are parsing a non-delimited argument entry
1487 Frame& frame = frames.top();
1488 assert(frame.pos < frame.entry.pattern.size());
1490 if (frame.entry.pattern[frame.pos].category == TToken::PARAMETER)
1492 // As by the TeX parsing rules of undelimited parameters,
1493 // empty spaces are ignored
1494 if (token.category != TToken::SPACE)
1496 // We need to increase the frame position here, becase inside
1497 // process the function advance will be called. At that point
1498 // it will be important for the parser to know that the entry
1499 // has been completed in order to place the cursor correctly
1500 // in the next position
1505 else if (frame.entry.pattern[frame.pos] == token)
1507 // The token has been accepted
1509 if (frame.pos < frame.entry.pattern.size() &&
1510 frame.entry.paramDelimited(frame.pos))
1512 // If the next is a delimited argument we have to place
1513 // the phantom group with the cursor inside
1514 TNode g = doc.createG();
1523 // There is a mismatch. Emit an error and ignore the token?
1524 logger.debug("parser: token ignored: " + token.value);
1532 logger.warning("ignored token");
1535 if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1542 if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1548 TPushParser::advance(const TNode& node)
1555 logger.error("wrong TML tree");
1557 else if (node.parent().isG())
1559 TNode next = node.next();
1560 if (next) next.insert(cursor);
1561 else node.parent().append(cursor);
1563 else if (node.parent().isC())
1565 assert(!frames.empty());
1566 if (frames.top().pos == frames.top().entry.pattern.size())
1568 if (frames.top().entry.rightOpen)
1570 // we have to remove the frame from the stack
1572 advance(node.parent().parent());
1577 advance(node.parent());
1580 else if (frames.top().entry.paramDelimited(frames.top().pos))
1582 // the next argument is delimited, so we have to create a phantom group
1583 TNode g = doc.createG();
1585 node.parent().append(g);
1589 // the next argumet is not delimited, so we have to append the cursor
1591 node.parent().append(cursor);
1594 else advance(node.parent());
1599 * This version handles the case in which we have to
1600 * create a delimited argument
1603 TPushParser::advance(const TNode& node)
1606 TNode parent = node.parent();
1608 ; // nothing to do, the cursor is not in the document any more
1609 else if (parent.isG())
1611 TNode next = node.next();
1612 if (next) next.insert(cursor);
1613 else parent.append(cursor);
1615 else if (parent.isC())
1621 Frame& frame = frames.top();
1622 if (frame.pos == frame.entry.pattern.size())
1627 else if (frame.entry.paramDelimited(frame.pos))
1629 // the next argument is delimited, so we have to create a phantom group
1630 // with the cursor inside. We have to remember that, since we are here,
1631 // the cursor has been removed
1632 TNode g = doc.createG();
1638 // otherwise, the next MACRO's argument is not delimited, so we just
1639 // append the cursor to the MACRO
1640 parent.append(cursor);
1644 else if (parent.is("math"))
1654 TPushParser::advance(const TNode& node)
1657 TNode parent = node.parent();
1659 ; // nothing to do, the cursor is not in the document any more
1660 else if (parent.isG())
1662 TNode next = node.next();
1663 if (next) next.insert(cursor);
1664 else parent.append(cursor);
1666 else if (parent.isC())
1672 Frame& frame = frames.top();
1673 if (frame.pos == frame.entry.pattern.size())
1679 parent.append(cursor);
1682 else if (parent.is("math"))
1691 TPushParser::setCursorHint(const std::string& c)
1693 if (cursor["val"] != c)
1696 if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1701 TPushParser::hideCursor()
1703 if (hiddenCursor++ == 0)
1705 cursor["visible"] = "0";
1706 if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1714 TPushParser::showCursor()
1716 if (hiddenCursor > 0 && --hiddenCursor == 0)
1718 cursor["visible"] = "1";
1719 if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1729 if (APushParser::thaw() && factory && doc.dirtyNode())
1731 factory->documentModified(doc);