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";
33 if (cursor.parent()) cursor.remove();
35 doc.root().append(cursor);
36 if (factory) factory->documentModified(doc);
40 TPushParser::PRIME() const
42 const TDictionary::Entry entry = dictionary.find("prime");
43 if (entry.cls == TDictionary::OPERATOR) return entry.value;
48 TPushParser::do_begin()
50 TNode parent = cursor.parent();
51 if (parent.isC() && dictionary.find(parent.nameC()).table)
53 TNode row = doc.create("row");
54 TNode cell = doc.create("cell");
55 TNode g = doc.createG();
63 TNode g = doc.createG(nextId++);
70 TPushParser::correctBrace()
72 // this method MUST be invoked when the cursor is child of a
73 // phantom group, which in turn is the last rightOpen MACRO's child.
74 // The only way to exit from a rightOpen MACRO is opening a group before
75 // inserting the MACRO and, once the MACRO is completely inserted, closing
77 // This method return true if the condition above is true. False, otherwise.
78 assert(cursor.parent() && cursor.parent().isG() && !cursor.parent().hasId());
79 TNode parent = cursor.parent();
80 assert(parent.parent() && parent.parent().isC());
81 assert(!frames.empty());
82 Frame& frame = frames.top();
83 assert(frame.entry.rightOpen);
84 assert(parent.parent().last() == parent);
86 TNode c = parent.parent();
89 TNode node = c.parent();
92 if (node.isG() && node.hasId())
94 // in this case, the rightOpen MACRO is a child of a group with id.
95 // So, the '}' is correct
101 // the MACRO is a phantom group's child. We have to control why we
102 // have this phantom group
103 TNode nodeParent = node.parent();
104 if (nodeParent && nodeParent.isC())
106 // we have to control the nature of this MACRO
107 const TDictionary::Entry& entry = dictionary.find(nodeParent.nameC());
108 if (entry.rightOpen && node == nodeParent.last())
110 // in this case we have to re-iterate the process
111 node = nodeParent.parent();
119 // at the moment we assume that a MACRO cannot be child of an element other than a group
129 TPushParser::do_end()
131 TNode parent = cursor.parent();
132 if (parent && parent.isG() && parent.hasId())
134 // normal closing brace for an explicitly open group
138 else if (parent && parent.isG() && parent.parent() && parent.parent().is("cell"))
140 assert(!frames.empty());
141 // closing brace for a structure in which & or \cr have been used
142 TNode row = parent.parent().parent();
143 assert(row && row.is("row"));
144 assert(row.parent());
147 else if (parent && parent.isG() && !parent.hasId() && parent.parent() && !parent.parent().is("math"))
149 // In this case, we have to control the cursor's grand parent.
150 TNode gparent = parent.parent();
152 if (gparent.isC() && gparent.last() == parent)
154 // a frame MUST be in the stack
155 assert(!frames.empty());
157 // we have to control the nature of this macro
158 if (frames.top().entry.rightOpen)
160 // in this case, the '}' character is the proper way to exit from the phantom group, and
161 // in particular, this character means that the user wants to exit from the MACRO.
162 // A rightOpen MACRO MUST be descendant of a group with Id. This '}' is the closing brace of this
163 // group. So, we have to control if this group exists. This groyp could exist, but this MACRO could
164 // be another MACRO's child, so we have to control this last MACRO recursively. This recurive control
165 // is done by the correctBrace method.
168 // the '}' is not correct
169 logger.warning("nothing to close");
180 logger.error("closing brace ignored");
185 // at the moment, a phantom group with cursor can be a MACRO's child or a cell's child, and these cases
186 // are handled in other blocks of code.
187 logger.error("do_end: strange TML tree");
192 // In this case, there is a redundant '}', so we can ignore it and
194 logger.warning("There is so no corresponding'{'");
202 TPushParser::do_end()
204 TNode parent = cursor.parent();
205 if (parent && parent.isG() && parent.hasId())
207 // normal closing brace for an explicitly open group
211 else if (parent && parent.isG() && parent.parent() && parent.parent().is("cell"))
213 assert(!frames.empty());
214 // closing brace for a structure in which & or \cr have been used
215 TNode row = parent.parent().parent();
216 assert(row && row.is("row"));
217 TNode table = row.parent();
221 else if (parent && parent.isG() && !parent.hasId() && parent.parent() && !parent.parent().is("math"))
223 // closing brace for a right-open macro (like \over)
224 // It's not sure that we want to exit from a phantom group with a
225 // '}', because, to enter in the phantom group the user didn't insert a
232 // In this case, there is a redundant '}', so I think we can ignore it and
234 logger.warning("There is so no corresponding'{'");
241 TPushParser::do_shift()
243 TNode parent = cursor.parent();
245 if (parent.is("tex"))
247 TNode math = doc.create("math", nextId++);
248 TNode g = doc.createG();
249 cursor.replace(math);
253 else if (parent.isG() && !parent.hasId() && parent.parent() && parent.parent().is("math"))
257 // there is something before the cursor, hence this is the
258 // closing math shift
259 if (parent.parent()["display"] != "1")
261 // one math shift is enough to close it
266 // we need two closing math shifts
267 //cursor.remove(); ??
268 parent.parent().append(cursor);
271 else if (parent.parent()["display"] != "1")
273 // there is nothing before the cursor, and the math is not
274 // in display mode, so this must be a double math shift
275 parent.parent()["display"] = "1";
279 parent.parent().append(cursor);
282 else if (parent.is("math"))
288 logger.error("parser: math shift");
293 TPushParser::do_align()
295 TNode parent = cursor.parent();
296 if (parent && parent.isG() && parent.hasId())
298 // alignment tab used for the first time inside a group
299 TNode row = doc.create("row");
300 TNode cell = doc.create("cell");
301 TNode g = doc.createG();
304 g.append(parent.first(), cursor);
306 else if (parent && parent.isG() && parent.parent().is("cell"))
308 // alignment tab used within a cell
309 TNode oldCell = parent.parent();
310 assert(oldCell && oldCell.is("cell"));
311 TNode row = oldCell.parent();
312 assert(row && row.is("row"));
313 TNode cell = doc.create("cell");
314 if (oldCell.next()) oldCell.next().insert(cell);
315 else row.append(cell);
316 TNode g = doc.createG();
322 logger.error("alignment tab used outside matrix");
327 TPushParser::do_eol()
329 //if (cursor.parent()) cursor.remove();
333 TPushParser::do_parameter(const std::string& p)
339 TPushParser::do_subscript()
341 TNode parent = cursor.parent();
344 TNode prev = cursor.prev();
347 TNode elem = doc.create("sb", nextId++);
348 TNode g = doc.createG();
349 cursor.replace(elem);
355 TNode elem = doc.create("sb", nextId++);
361 else if (parent.isSb() && cursor == parent[1])
363 if (parent["under"] == "1") logger.error("already under");
364 else parent["under"] = "1";
369 TPushParser::do_superscript()
371 TNode parent = cursor.parent();
374 TNode prev = cursor.prev();
377 TNode elem = doc.create("sp", nextId++);
378 TNode g = doc.createG();
379 cursor.replace(elem);
385 TNode elem = doc.create("sp", nextId++);
391 else if (parent.isSp() && cursor == parent[1])
393 if (parent["over"] == "1") logger.error("already over");
394 else parent["over"] = "1";
399 TPushParser::do_space(const std::string&)
401 logger.debug("do_space");
405 TPushParser::do_letter(const std::string& s)
407 //TNode parent = cursor.parent();
408 TNode elem = doc.createI(s, nextId++);
409 cursor.replace(elem);
414 TPushParser::do_digit(const std::string& s)
416 TNode parent = cursor.parent();
417 TNode prev = cursor.prev();
418 if (prev && parent.isG() && prev.is("n"))
420 TNode elem = doc.createN(prev.value() + s, nextId++);
425 TNode elem = doc.createN(s, nextId++);
426 cursor.replace(elem);
432 TPushParser::isPrimes(const TNode& node) const
435 return node.isG() && node.last() && node.last().is("o") && node.last()["val"] == PRIME();
439 TPushParser::do_apostrophe()
441 if (cursor.parent() && cursor.parent().isG())
443 if (TNode prev = cursor.prev())
445 if (prev.isSp() && prev[1] && isPrimes(prev[1]))
446 prev[1].append(doc.createO(PRIME(), nextId++));
447 else if (prev.isSb() && prev[0] &&
448 prev[0].isSp() && prev[0][1] &&
449 isPrimes(prev[0][1]))
450 prev[0][1].append(doc.createO(PRIME(), nextId++));
453 TNode elem = doc.create("sp");
454 TNode g = doc.createG();
458 g.append(doc.createO(PRIME(), nextId++));
464 logger.error("parser: you have to type one identifier before the ''");
474 TPushParser::do_other(const std::string& s)
482 /*cout << "TPushParser::do_other " << s << endl;
483 cout << "DOCUMENT: " << static_cast<GdomeNode*>(cursor.element().get_ownerDocument()) << endl;*/
484 TNode elem = doc.createT("o", s, nextId++);
485 cursor.replace(elem);
492 TPushParser::do_active(const std::string&)
498 TPushParser::do_comment()
506 TNode parent = cursor.parent();
507 if (parent && parent.isG() &&
508 parent.parent() && parent.parent().is("cell") &&
509 parent.parent().parent() && parent.parent().parent().is("row"))
511 TNode oldRow = parent.parent().parent();
513 TNode table = oldRow.parent();
515 TNode row = doc.create("row");
516 TNode cell = doc.create("cell");
517 TNode g = doc.createG();
518 if (oldRow.next()) oldRow.next().insert(row);
519 else table.append(row);
527 TPushParser::do_control(const std::string& name)
529 if (name == "cr") do_cr();
532 TNode parent = cursor.parent();
533 const TDictionary::Entry& entry = dictionary.find(name);
536 case TDictionary::IDENTIFIER:
538 TNode t = doc.createI(entry.value, nextId++);
544 case TDictionary::OPERATOR:
546 TNode t = doc.createO(entry.value, nextId++);
552 case TDictionary::NUMBER:
554 TNode t = doc.createN(entry.value, nextId++);
560 case TDictionary::MACRO:
562 TNode m = doc.createC(name, nextId++);
565 if (entry.leftOpen && entry.rightOpen)
567 assert(entry.pattern.empty());
568 assert(parent.isG());
569 TNode g1 = doc.createG();
570 g1["left-open"] = "1";
571 g1.append(parent.first(), m);
573 TNode g2 = doc.createG();
576 frames.push(Frame(entry));
578 else if (entry.leftOpen)
580 assert(parent.isG());
581 TNode g = doc.createG();
582 g["left-open"] = "1";
583 g.append(parent.first(), m);
587 else if (entry.rightOpen)
589 assert(entry.pattern.empty());
590 assert(parent.isG());
591 TNode g = doc.createG();
594 frames.push(Frame(entry));
596 else if (!entry.pattern.empty())
600 frames.push(Frame(entry));
602 if (entry.paramDelimited(0))
604 TNode g = doc.createG();
613 // error, but we could handle this very easily
614 logger.error(" parser:but we could handle this easily");
620 case TDictionary::UNDEFINED:
622 logger.error("parser: using undefined macro " + name);
623 TNode m = doc.createC(name, nextId++);
635 TPushParser::gdelete_prev_token()
637 assert(cursor.prev());
638 assert(cursor.parent());
639 TNode prev = cursor.prev();
640 assert(prev.is("i") || prev.is("o") || prev.is("n"));
642 // the control below is designed to handle the case in which val have more than one unicode character
643 DOM::UCS4String ucs4val(prev.element().getAttribute("val"));
644 if ((prev.is("i")) || (ucs4val.length() <= 1) || prev.element().hasAttribute("name"))
647 prev.replace(cursor);
649 if (cursor.parent().isC())
651 // in this case we have removed an element of a MACRO.
652 // we can assert that this element was a non delimited argument
653 assert(!frames.empty());
654 Frame& frame = frames.top();
655 assert(frame.pos > 0);
662 ucs4val.erase(ucs4val.length() - 1, 1);
663 prev.element().setAttribute(DOM::GdomeString("val"), DOM::GdomeString(ucs4val));
669 TPushParser::gdelete_prev_script()
671 // this method deletes an sp or an sb preceding the cursor
672 assert(cursor.prev());
673 assert(cursor.parent());
674 TNode prev = cursor.prev();
675 assert(prev.is("sp") || prev.is("sb"));
678 // we can invoke the gdelete_prev, because a sp (sb) MUST have two children
683 TPushParser::gdelete_prev_group()
685 assert(cursor.prev() && cursor.prev().isG());
686 TNode prev = cursor.prev();
690 // a group could have no children, so the gdelete_prev is not appropriate
691 // so, this method is not equivalent to the one above
696 TPushParser::gdelete_prev_macro()
698 assert(cursor.parent());
699 assert(cursor.prev());
700 TNode prev = cursor.prev();
703 const TDictionary::Entry& entry = dictionary.find(prev["name"]);
705 if (!entry.defined())
707 // We can assume that the user want to completely delete the undefined macro
709 prev.replace(cursor);
713 // we start to remove a MACRO. Different actions must be taken, based on the nature
714 // of the MACRO. In some cases, we can't remove the MACRO immediately, in other
715 // cases it's correct. In the first set of cases, we have to update the stack, pushing
716 // a frame in it with a correct value of pos, in the
717 // second one, we must not push a frame in the stack
721 // In this fragment of code we also handle the leftOpen && rightOpen MACRO.
722 // if the control element is rightOpen, the cursor should be placed after
723 // the last child of the control element's last child, and than, we try to remove something.
724 // A frame MUST be pushed in the stack, because we dont' know if the following actions
725 // will completely remove the MACRO.
726 frames.push(Frame(entry));
728 // Since the MACRO is rightOpen, the last child of the MACRO must be a phantom group
729 assert(prev.last().isG() && !prev.last().hasId());
732 prev.last().append(cursor);
734 // the gdelete_prev is not appropriate, because the last child of the MACRO could have no children
735 do_gdelete_phantom_group();
737 else if (entry.leftOpen)
739 // the leftOpen MACRO MUST have one and only one child, which MUST be a phantom group
740 // In this case, we do not have to push a frame in the stack, because we remove the
741 // MACRO immediately, substituting it with the content of the phantom group.
742 // At the moment, we don't remove the last child of the phantom group, but
743 // it's not clear if it's the correct behavior of the graphical deleting.
744 // To delete it, just remove the comment of the last instruction of the
745 // if (g.size()) block.
746 assert(prev.first());
747 assert(prev.first().isG());
748 assert(prev.first() == prev.last());
750 TNode g = prev.first();
753 // in this case, the phantom group has at least one child, so we can call the
756 prev.replace(g.first(), TNode());
761 // otherwise, the phantom group has no children, so we remove it, also the MACRO.
764 prev.replace(cursor);
767 else if (!entry.pattern.empty())
769 // we have to start to remove a MACRO which accepts arguments.
770 // If the MACRO accepts arguments, the MACRO has at least one child
771 assert(prev.size() >= 1);
773 // Differnt actions must be taken, based on the nature of the last child
774 // of the MACRO. We have to distinguish the case in which it's a delimited argument,
775 // frome the one in which it's a not delimited argument.
776 if (prev.last().isG() && !prev.last().hasId())
778 // the last argument of the MACRO is a delimited argumet. We ideally remove
779 // the sequence of delimiters
781 prev.last().append(cursor);
782 // we have to push a frame with a correct value of pos
783 assert(entry.previousParam(entry.pattern.size()) != entry.pattern.size());
785 unsigned sequence_length = entry.pattern.size() - entry.previousParam(entry.pattern.size()) - 1;
786 unsigned p = entry.pattern.size() - sequence_length - 1;
787 // now, p is the correct value of pos, and we can push the frame.
788 frames.push(Frame(entry, p));
792 // in this case, the last child of the MACRO is not a delimited argument, so we try
793 // to remove it, but we have to take differnt actions if the MACRO is a table with rows or not.
795 if (entry.table == 1 && prev.last().is("row"))
797 // in this case the cursor should be appended to the group associated to
798 // the last cell of the last row of the table
799 assert(prev.last().last().is("cell") && prev.last().last().first().isG());
800 prev.last().last().first().append(cursor);
807 // we push a frame in the stack with a correct value of member pos.
808 // This correct value is the size of the pattern, because we have to START to delete
809 // a MACRO. It means that all of the MACRO's arguments have been inserted.
810 frames.push(Frame(entry, entry.pattern.size()));
814 // in this case we try to remove the last child of the MACRO.
819 // this block of code will never be executed, because the MACRO accepts arguments,
820 // so a MACRO's child MUST exist. But, we can handle this case, emitting a warning
821 logger.warning("Parser: the TML tree is in a strange state, but a good state will be generated");
824 } // end of the else of the if (prev.last().isG() && !prev.last().hasId())
826 } // end of if (!entry.pattern.empty())
829 // if we are here, the MACRO preceding the cursor, is !(rightOpen || leftOpen),
830 // and has no pattern. It means that it has no children.
831 // We can replace it with the cursor
832 assert(prev.size() == 0);
834 prev.replace(cursor);
836 } // end of defined MACRO
841 TPushParser::gdelete_prev()
843 // if in this function, the prev of cursor does exist, also the parent and we want a graphical deleting.
845 assert(cursor.prev());
846 assert(cursor.parent());
848 TNode prev = cursor.prev();
850 if (prev.is("i") || prev.is("o") || prev.is("n"))
852 gdelete_prev_token();
854 else if (prev.isSp() || prev.isSb())
856 gdelete_prev_script();
860 gdelete_prev_group();
864 // here, we also treat the case in which the MACRO is a table
865 gdelete_prev_macro();
869 // not handled. Future cases...
875 TPushParser::rgreplace_father(void)
877 // this method MUST only be invoked, when the cursor
878 // is the only child of a group with id. This function
879 // replaces the group with the cursor. But if the new parent
880 // is a group with id and the cursor is the only child of the
881 // group, the new parent is replaced...and so on.
882 // r stands for recursive, g stands for graphical.
883 assert(cursor.parent());
884 assert(cursor.parent().isG() && cursor.parent().hasId());
886 TNode parent = cursor.parent();
888 while (parent.isG() && parent.hasId() && (parent.first() == cursor))
890 parent.replace(cursor);
891 parent = cursor.parent();
896 // in this case we have removed a MACRO's child.
897 // So, we have to update the member pos of the frame in the stack
898 // We can assert that this MACRO accepts arguments.
899 assert(!frames.empty());
900 Frame& frame = frames.top();
901 assert(frame.pos > 0);
907 TPushParser::do_gdelete_script()
909 // If we are here, the cursor is child of a script (sp or sb) and
910 // this means that a prev does exist and that there is one and only one
911 // element preceding the cursor. The sp's (or sb's) parent
912 // MUST NOT be a MACRO.
913 // The element preceding the cursor is the base of the script.
915 assert(cursor.parent() && (cursor.parent().isSp() || cursor.parent().isSb()));
916 TNode parent = cursor.parent();
918 assert(parent.size() == 2);
919 assert(parent.parent() && !parent.parent().isC());
921 TNode prev = cursor.prev();
923 if (prev.isG() /*&& !prev.hasId()*/ && (prev.size() == 0))
925 // in this case, the script's base is a group with no elements, so
926 // we have to remove the entire MACRO, replacing it with the cursor.
927 // This situation occurs when the user type something like this
934 parent.replace(cursor);
936 // if the new parent is a group with Id and the cursor is the only
937 // element of this group, we have to remove the group. These controls are made
938 // in the method rgreplace_father().
939 if (cursor.parent().isG() && cursor.parent().hasId()) rgreplace_father();
943 // in this case, the prev has to replace the script,
944 // and the cursor has to be placed after the prev.
945 assert(prev.hasId());
946 parent.replace(prev);
947 prev.parent().append(cursor);
948 // now prev have a preceding element
949 assert(cursor.parent().size() > 1);
952 } // end of method do_gdelete_script
955 TPushParser::do_gdelete_macro()
957 // If we are here, the cursor is a child of a MACRO and this means
958 // that there is an open frame for the control element
959 // and this element is closed at either side (no leftOpen no rightOpen)
960 // and the MACRO is waiting for a not delimited argument, so
961 // we can assert that frame.entry.pattern.size() >= 1
962 assert(cursor.parent() && cursor.parent().isC());
963 TNode parent = cursor.parent();
965 assert(!frames.empty());
966 Frame& frame = frames.top();
967 assert(frame.entry.pattern.size() >= 1);
969 // we have to take different actions, based on if a preceding element exists
971 TNode prev = cursor.prev();
974 // in this case, a prev does not exist, so the actions of deleting means
975 // that we have to remove the MACRO. So we have to pop the stack.
976 // Being here also means that the MACRO is waiting for the first argument
977 // (which is not delimited), but we don't mind about it.
978 assert(frame.pos == 0);
979 parent.replace(cursor);
982 // if the new parent is a group with Id, and has no elements other than the
983 // cursor, we can remove it, because it' a graphical deleting
984 if (cursor.parent() && cursor.parent().isG() && cursor.parent().hasId())
986 else if (cursor.parent().isC())
988 // We have assumed that a MACRO cannot be a MACRO's child.
989 // At the moment, this assumption is valid, but in a future
990 // it might be false.
991 assert(!frames.empty());
992 Frame& frame = frames.top();
993 assert(frame.pos > 0);
999 // a prev does exist, we have to control if it's a delimited argument or not.
1000 if (prev.isG() && !prev.hasId())
1002 // in this case, prev is a delimited argument, so we have
1003 // to ideally remove the sequence of delimiters
1004 Frame& frame = frames.top();
1005 assert(frame.pos > 1);
1007 prev.append(cursor);
1008 assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
1010 // these 3 lines of code update the member pos.
1011 unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
1012 assert(sequence_length);
1013 frame.pos = frame.pos - sequence_length - 1;
1017 // the prev is not a delimited argument, so we have to try to remove it.
1018 // We "try", because the prev might be a something that
1019 // a simple delete cannot remove completely
1027 TPushParser::do_gdelete_groupId()
1029 // if we are here, the cursor's parent is a group with Id
1030 assert(cursor.parent() && cursor.parent().isG() && cursor.parent().hasId());
1031 TNode parent = cursor.parent();
1033 // we have to take different actions based on if the cursor has a preceding
1035 TNode prev = cursor.prev();
1038 // the cursor has a preceding element, so we try to remove it
1041 // We control if the group has to be removed, because the cursor
1042 // might be the only element of the group.
1043 if ((parent.first() == cursor) && parent.isG() && parent.hasId())
1049 // the cursor has no preceding elements, so we have to remove the
1053 // we have to re-start the process, because it' a graphical delete
1057 } // end of method do_gdelete_groupId()
1060 TPushParser::do_gdelete_phantom_group()
1062 // if we are here, the cursor MUST be a child of a
1064 assert(cursor.parent() && cursor.parent().isG() && !cursor.parent().hasId());
1066 TNode parent = cursor.parent();
1068 // now we have to control if the cursor has a preceding element or not
1069 TNode prev = cursor.prev();
1072 // the cursor has a preceding element, so we try to remove it
1075 if (parent.size() == 1 && parent.parent().isSp())
1077 // in this case the gdelete_prev has removed the only element preceding the cursor.
1078 // If the phantom group is an sp's child, it means that the user has removed all \' in the
1080 // We can remove the phamtom group and the sp element. But we also can only remove the
1081 // phantom group, giving the user the possibility of inserting an exponent.
1082 // At the moment, we remove the sp element (and the phantom group), because it may be the correct
1083 // behavior of a graphical deleting.
1085 parent.replace(cursor);
1086 // now we have an sp element with two children: the first child (we don't know anything about it)
1088 assert(cursor.parent().size() == 2);
1090 // to delete the script we can invoke the do_gdelete_script(), which will do all controls we need.
1091 // To give the possibility of insetring an exponent, just remove the following istruction.
1092 do_gdelete_script();
1094 else if (parent.parent().isSp())
1096 // in this case we have to place the cursor after the sp element
1098 assert(parent.parent().parent());
1099 parent.parent().parent().append(cursor);
1104 // in this case the cursor is the only element of the phantom group,
1105 // so we have to remove it. But, a phantom group has a special role,
1106 // so we have to control the grand father of the cursor.
1107 TNode gfather = parent.parent();
1110 // If here, the TML tree is in an inconsistent state
1111 logger.error("do_gdelete_phantom: TML tree in a inconsistent state");
1113 else if (gfather.isC())
1115 // in this case the phantom group is child of a MACRO.
1116 // We have to control the nature of this MACRO.
1117 assert(!frames.empty());
1118 Frame& frame = frames.top();
1120 if (frame.entry.leftOpen && frame.entry.rightOpen)
1122 // in this case, the cursor'parent is in the second and last child
1123 // of the MACRO. We can assert that the grand father has two
1124 // children, which are both phantom groups
1125 assert(gfather.size() == 2);
1126 assert((gfather.last() == parent) && (gfather.first().isG() && !gfather.first().hasId()));
1127 assert(frame.pos == 0);
1129 TNode ggfather = gfather.parent();
1133 // we have to replace the gfather with the elements of its first child, but this group may have no
1135 if (gfather.first().size())
1137 gfather.replace(gfather.first().first(), TNode());
1138 ggfather.append(cursor);
1142 // in this case, the MACRO has to be replaced with the cursor
1143 gfather.first().remove();
1144 gfather.replace(cursor);
1146 // now we have the situation preceding the insertion of the leftOpen and rightOpen MACRO.
1147 // this MACRO no longer exists.
1150 else if (frame.entry.rightOpen)
1152 // the user has inserted a rightOpen MACRO, and now, this MACRO has no children (excluding the
1153 // phantom group), so we remove the MACRO.
1154 // We can assert that cursor's parent is the only child of the MACRO
1155 assert(gfather.size() == 1);
1156 assert(frame.pos == 0);
1159 gfather.replace(cursor);
1161 // now we have the situation preceding the rightOpen MACRO, so we have to pop the frame
1164 else if (frame.entry.leftOpen)
1166 // this situation will never occur.
1167 logger.error("the parser has generated a wrong TML tree");
1169 else if (!frame.entry.pattern.empty())
1171 // the MACRO accepts arguments, and the phantom group in which
1172 // the cursor is, rappresents a delimited argument
1173 // We have to control if the cursor's parent has a preceding element,
1175 TNode uncle = parent.prev();
1178 // the parent is the only element of the MACRO.
1179 // we can assert that frame.pos == 0.
1180 // In this case we can replace the MACRO with the cursor
1181 assert(frame.pos == 0);
1184 gfather.replace(cursor);
1189 // the parent has a preceding element. Now we have
1190 // to control if the uncle is a delimited argument or not.
1191 if (uncle.isG() && !uncle.hasId())
1193 // the uncle is a delimited argument. So we have to ideally
1194 // remove the sequence of delimiters.
1195 assert(frame.pos > 1);
1196 unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
1197 assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
1198 assert(sequence_length);
1199 // sequence_length is the length of the delimiters sequence which separates
1200 // the current parameter and the previous parameter
1201 frame.pos = frame.pos - sequence_length - 1;
1204 uncle.append(cursor);
1208 // the uncle is a not delimited argument, so we try to remove it.
1210 parent.replace(cursor);
1211 parent = cursor.parent(); // i update the parent (it should be the MACRO)
1212 assert(parent.isC());
1214 // now we try to remove the uncle (now it' the preceding element)
1217 } // this is the else's end, that handles the case in which an uncle exists
1218 } // end of if (!frame.entry.pattern.empty())
1221 // the entry has no arguments, is not rightOpen and is not leftOpen.
1222 logger.warning("parser: TML tree in a strange state, but we try to recover from it");
1223 if (gfather.size() == 1)
1227 gfather.replace(cursor);
1231 logger.warning("parser: TML tree in a very strange state, but we try to recover from it");
1233 gfather.replace(cursor);
1236 } // end of if (gfather.isC())
1237 else if (gfather.is("cell"))
1239 // A table is a control sequence, so there is a frame in the stack
1240 assert(!frames.empty());
1241 assert(frames.top().pos == 1);
1242 assert(frames.top().entry.table == 1);
1244 // a cell MUST be a row's child, which in turn is a table's child
1245 assert(gfather.parent() && gfather.parent().is("row") && gfather.parent().parent());
1246 TNode row = gfather.parent();
1248 // in this case the cell has no element, so the user wants to delete this cell.
1249 TNode prev_cell = gfather.prev();
1253 // now the cell no longer exists
1257 // in this case, the cell is the only cell in the row.
1258 // So, we assume that the user wants to delete the entire row.
1259 TNode table = row.parent();
1260 TNode prev_row = row.prev();
1265 // the row was the only child of the table.
1266 // so we have to delete the entire table
1267 assert(table.parent());
1268 TNode parent_table = table.parent();
1271 parent_table.append(cursor);
1275 // there are other rows (one or more)
1276 assert(prev_row.is("row"));
1277 assert(prev_row.last());
1278 TNode last_cell = prev_row.last();
1279 assert(last_cell.is("cell"));
1280 assert(last_cell.size() == 1);
1281 assert(last_cell.first().isG() && !last_cell.first().hasId());
1282 last_cell.first().append(cursor);
1284 } // end of if (!prev_cell)
1287 // being here means that there is a previous cell,
1288 // so we append the cursor to group.
1289 assert(prev_cell.size() == 1);
1290 assert(prev_cell.first().isG() && !prev_cell.first().hasId());
1291 prev_cell.first().append(cursor);
1293 } // end of if (gfather.is("cell"))
1294 else if (gfather.isSp())
1296 // in this case, the user typed a \'. So this phantom group
1297 // contained a sequence of \'.
1298 // Maybe in this part will never be used, because, if we delete last \' in the
1299 // phantom group, we remove the phantom group also
1301 // In any case, if we are here we have two possibilities:
1302 // we can delete the phantom group;
1303 // we can delete the superscript.
1304 // At the moment we implement the first solution. To implement the second one, just remove
1305 // the line code after the logger.warning and remove comments from the remaining lines
1306 logger.warning("parser: TML tree in a strange state, we try to recover from it");
1307 parent.replace(cursor);
1311 //gfather.replace(cursor);
1313 else if (gfather.is("math"))
1315 // in this case we ignore the user's will of deleting
1316 // but we can decide to remove the math mode.
1317 logger.info("Parser: nothing to delete");
1321 // cursor's grand father is undefined
1322 logger.error("parser: TML tree is in a unknown state");
1324 } // end of the else of the if (prev)
1330 TPushParser::do_gdelete()
1332 // we have to handle the case in wich the cursor has a parent or not
1333 if (!cursor.parent())
1335 // it's not a good situation...at the moment we do not take actions
1336 logger.error("TML tree not well structured");
1340 // a parent exists. We have to take differnt actions, based on the nature of
1342 TNode parent = cursor.parent();
1343 if (parent.is("math"))
1345 // we ca do two thing...we can remove the math mode (it implies controlling the display attribute), we can do nothing
1346 // At the moment, the user's will of deleting is simply ignored
1347 logger.warning("nothing to delete");
1349 else if (parent.isG())
1351 // the cursor's parent is a group. We have to control if it's a phantom group or not
1354 do_gdelete_groupId();
1358 do_gdelete_phantom_group();
1360 } // end of parent is group
1361 else if (parent.isC())
1364 } // end of parent is a MACRO
1365 else if (parent.isSp() || parent.isSb())
1367 do_gdelete_script();
1368 } // end of parent is sp or sb
1369 } // end of the else which consider the case in which parent exists
1371 } // end of method do_gdelete
1374 TPushParser::process(const TToken& token)
1376 switch (token.category)
1378 case TToken::BEGIN: do_begin(); break;
1379 case TToken::END: do_end(); break;
1380 case TToken::SHIFT: do_shift(); break;
1381 case TToken::ALIGN: do_align(); break;
1382 case TToken::EOL: do_eol(); break;
1383 case TToken::PARAMETER: do_parameter(token.value); break;
1384 case TToken::SUPERSCRIPT: do_superscript(); break;
1385 case TToken::SUBSCRIPT: do_subscript(); break;
1386 case TToken::SPACE: do_space(token.value); break;
1387 case TToken::LETTER: do_letter(token.value); break;
1388 case TToken::DIGIT: do_digit(token.value); break;
1389 case TToken::OTHER: do_other(token.value); break;
1390 case TToken::ACTIVE: do_active(token.value); break;
1391 case TToken::COMMENT: do_comment(); break;
1392 case TToken::CONTROL: do_control(token.value); break;
1397 TPushParser::push(const TToken& token)
1399 TNode parent = cursor.parent();
1400 // If the cursor has no parent then it is detached from the editing
1401 // tree, which means this token will be ignored
1404 // If the parent is a phantom group and the grand-parent is a
1405 // control sequence, there are two cases:
1406 // a. we are parsing a delimited argument of a entry
1407 // b. we are parsing a side of a right- or left-open entry
1408 if (parent.isG() && !parent.hasId() && parent.parent().isC())
1410 // There must be an open frame, for the grand-parent is a control sequence
1411 assert(!frames.empty());
1412 Frame& frame = frames.top();
1413 if (!frame.entry.pattern.empty())
1415 // The entry pattern is not empty. By our conventions this means
1416 // the entry cannot be open at either end, hence we are parsing
1417 // a delimited argument
1418 assert(frame.pos + 1 < frame.entry.pattern.size());
1419 assert(frame.entry.pattern[frame.pos + 1].category != TToken::PARAMETER);
1420 if (frame.entry.pattern[frame.pos + 1] == token)
1422 // The token matches with a delimiter of the argument,
1423 // hence we increment the frame.pos
1426 if (frame.entry.lastDelimiter(frame.pos))
1428 // this delimiter is the last one for the argumet,
1429 // so the argument is completed
1433 if (frame.pos == frame.entry.pattern.size())
1435 // This token has completed the entry
1438 else if (frame.entry.paramDelimited(frame.pos))
1440 // For the next is a delimited argument we have to place
1441 // a suitable phantom group with the cursor inside
1442 TNode g = doc.createG();
1443 parent.parent().append(g);
1447 parent.parent().append(cursor);
1452 // Delimiter mismatch.
1453 if (frame.entry.pattern[frame.pos].category != TToken::PARAMETER)
1455 // in this case, there is a sequence of delimiters that delimitates
1456 // the argument, and the user has correctly inserted a portion of this
1457 // sequence, but now has inserted a wrong delimiter.
1458 // Here, there are some possibilities:
1459 // - ignore the token, and wait for the correct delimiter
1460 // - ignore the token, wait for the correct delimiter and emit an error
1461 // At the moment, we implement the second possibily
1462 logger.error("parser: it's not the correct delimiter...you have to type " + frame.entry.pattern[frame.pos + 1].value);
1466 // in this case, the sequence of delimiters is composed of one
1467 // delimiter. It means that we have to process the token
1474 // The entry pattern is empty, hence we are parsing a right-open
1475 // entry. What happens if we actually are in the left side?
1476 // This could happen only when re-editing an entered expression
1478 assert(frame.entry.rightOpen);
1482 else if (parent.isC())
1484 // We are parsing a non-delimited argument entry
1486 Frame& frame = frames.top();
1487 assert(frame.pos < frame.entry.pattern.size());
1489 if (frame.entry.pattern[frame.pos].category == TToken::PARAMETER)
1491 // As by the TeX parsing rules of undelimited parameters,
1492 // empty spaces are ignored
1493 if (token.category != TToken::SPACE)
1495 // We need to increase the frame position here, becase inside
1496 // process the function advance will be called. At that point
1497 // it will be important for the parser to know that the entry
1498 // has been completed in order to place the cursor correctly
1499 // in the next position
1504 else if (frame.entry.pattern[frame.pos] == token)
1506 // The token has been accepted
1508 if (frame.pos < frame.entry.pattern.size() &&
1509 frame.entry.paramDelimited(frame.pos))
1511 // If the next is a delimited argument we have to place
1512 // the phantom group with the cursor inside
1513 TNode g = doc.createG();
1522 // There is a mismatch. Emit an error and ignore the token?
1523 logger.debug("parser: token ignored: " + token.value);
1531 logger.warning("ignored token");
1534 if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1541 if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1547 TPushParser::advance(const TNode& node)
1554 logger.error("wrong TML tree");
1556 else if (node.parent().isG())
1558 TNode next = node.next();
1559 if (next) next.insert(cursor);
1560 else node.parent().append(cursor);
1562 else if (node.parent().isC())
1564 assert(!frames.empty());
1565 if (frames.top().pos == frames.top().entry.pattern.size())
1567 if (frames.top().entry.rightOpen)
1569 // we have to remove the frame from the stack
1571 advance(node.parent().parent());
1576 advance(node.parent());
1579 else if (frames.top().entry.paramDelimited(frames.top().pos))
1581 // the next argument is delimited, so we have to create a phantom group
1582 TNode g = doc.createG();
1584 node.parent().append(g);
1588 // the next argumet is not delimited, so we have to append the cursor
1590 node.parent().append(cursor);
1593 else advance(node.parent());
1598 * This version handles the case in which we have to
1599 * create a delimited argument
1602 TPushParser::advance(const TNode& node)
1605 TNode parent = node.parent();
1607 ; // nothing to do, the cursor is not in the document any more
1608 else if (parent.isG())
1610 TNode next = node.next();
1611 if (next) next.insert(cursor);
1612 else parent.append(cursor);
1614 else if (parent.isC())
1620 Frame& frame = frames.top();
1621 if (frame.pos == frame.entry.pattern.size())
1626 else if (frame.entry.paramDelimited(frame.pos))
1628 // the next argument is delimited, so we have to create a phantom group
1629 // with the cursor inside. We have to remember that, since we are here,
1630 // the cursor has been removed
1631 TNode g = doc.createG();
1637 // otherwise, the next MACRO's argument is not delimited, so we just
1638 // append the cursor to the MACRO
1639 parent.append(cursor);
1643 else if (parent.is("math"))
1653 TPushParser::advance(const TNode& node)
1656 TNode parent = node.parent();
1658 ; // nothing to do, the cursor is not in the document any more
1659 else if (parent.isG())
1661 TNode next = node.next();
1662 if (next) next.insert(cursor);
1663 else parent.append(cursor);
1665 else if (parent.isC())
1671 Frame& frame = frames.top();
1672 if (frame.pos == frame.entry.pattern.size())
1678 parent.append(cursor);
1681 else if (parent.is("math"))
1690 TPushParser::setCursorHint(const std::string& c)
1692 if (cursor["val"] != c)
1695 if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1700 TPushParser::hideCursor()
1702 if (hiddenCursor++ == 0)
1704 cursor["visible"] = "0";
1705 if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1713 TPushParser::showCursor()
1715 if (hiddenCursor > 0 && --hiddenCursor == 0)
1717 cursor["visible"] = "1";
1718 if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1728 if (APushParser::thaw() && factory && doc.dirtyNode())
1730 factory->documentModified(doc);