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()
25 cursor = doc.create("cursor");
28 doc.root().append(cursor);
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++);
62 TPushParser::correctBrace()
64 // this method MUST be invoked when the cursor is child of a
65 // phantom group, which in turn is the last rightOpen MACRO's child.
66 // The only way to exit from a rightOpen MACRO is opening a group before
67 // inserting the MACRO and, once the MACRO is completely inserted, closing
69 // This method return true if the condition above is true. False, otherwise.
70 assert(cursor.parent() && cursor.parent().isG() && !cursor.parent().hasId());
71 TNode parent = cursor.parent();
72 assert(parent.parent() && parent.parent().isC());
73 assert(!frames.empty());
74 Frame& frame = frames.top();
75 assert(frame.entry.rightOpen);
76 assert(parent.parent().last() == parent);
78 TNode c = parent.parent();
81 TNode node = c.parent();
84 if (node.isG() && node.hasId())
86 // in this case, the rightOpen MACRO is a child of a group with id.
87 // So, the '}' is correct
93 // the MACRO is a phantom group's child. We have to control why we
94 // have this phantom group
95 TNode nodeParent = node.parent();
96 if (nodeParent && nodeParent.isC())
98 // we have to control the nature of this MACRO
99 const TDictionary::Entry& entry = dictionary.find(nodeParent.nameC());
100 if (entry.rightOpen && node == nodeParent.last())
102 // in this case we have to re-iterate the process
103 node = nodeParent.parent();
111 // at the moment we assume that a MACRO cannot be child of an element other than a group
121 TPushParser::do_end()
123 TNode parent = cursor.parent();
124 if (parent && parent.isG() && parent.hasId())
126 // normal closing brace for an explicitly open group
130 else if (parent && parent.isG() && parent.parent() && parent.parent().is("cell"))
132 assert(!frames.empty());
133 // closing brace for a structure in which & or \cr have been used
134 TNode row = parent.parent().parent();
135 assert(row && row.is("row"));
136 assert(row.parent());
139 else if (parent && parent.isG() && !parent.hasId() && parent.parent() && !parent.parent().is("math"))
141 // In this case, we have to control the cursor's grand parent.
142 TNode gparent = parent.parent();
144 if (gparent.isC() && gparent.last() == parent)
146 // a frame MUST be in the stack
147 assert(!frames.empty());
149 // we have to control the nature of this macro
150 if (frames.top().entry.rightOpen)
152 // in this case, the '}' character is the proper way to exit from the phantom group, and
153 // in particular, this character means that the user wants to exit from the MACRO.
154 // A rightOpen MACRO MUST be descendant of a group with Id. This '}' is the closing brace of this
155 // group. So, we have to control if this group exists. This groyp could exist, but this MACRO could
156 // be another MACRO's child, so we have to control this last MACRO recursively. This recurive control
157 // is done by the correctBrace method.
160 // the '}' is not correct
161 logger.warning("nothing to close");
172 logger.error("closing brace ignored");
177 // at the moment, a phantom group with cursor can be a MACRO's child or a cell's child, and these cases
178 // are handled in other blocks of code.
179 logger.error("do_end: strange TML tree");
184 // In this case, there is a redundant '}', so we can ignore it and
186 logger.warning("There is so no corresponding'{'");
194 TPushParser::do_end()
196 TNode parent = cursor.parent();
197 if (parent && parent.isG() && parent.hasId())
199 // normal closing brace for an explicitly open group
203 else if (parent && parent.isG() && parent.parent() && parent.parent().is("cell"))
205 assert(!frames.empty());
206 // closing brace for a structure in which & or \cr have been used
207 TNode row = parent.parent().parent();
208 assert(row && row.is("row"));
209 TNode table = row.parent();
213 else if (parent && parent.isG() && !parent.hasId() && parent.parent() && !parent.parent().is("math"))
215 // closing brace for a right-open macro (like \over)
216 // It's not sure that we want to exit from a phantom group with a
217 // '}', because, to enter in the phantom group the user didn't insert a
224 // In this case, there is a redundant '}', so I think we can ignore it and
226 logger.warning("There is so no corresponding'{'");
233 TPushParser::do_shift()
235 TNode parent = cursor.parent();
237 if (parent.is("tex"))
239 TNode math = doc.create("math", nextId++);
240 TNode g = doc.createG();
241 cursor.replace(math);
245 else if (parent.isG() && !parent.hasId() && parent.parent() && parent.parent().is("math"))
249 // there is something before the cursor, hence this is the
250 // closing math shift
251 if (parent.parent()["display"] != "1")
253 // one math shift is enough to close it
258 // we need two closing math shifts
259 //cursor.remove(); ??
260 parent.parent().append(cursor);
263 else if (parent.parent()["display"] != "1")
265 // there is nothing before the cursor, and the math is not
266 // in display mode, so this must be a double math shift
267 parent.parent()["display"] = "1";
271 parent.parent().append(cursor);
274 else if (parent.is("math"))
280 logger.error("parser: math shift");
285 TPushParser::do_align()
287 TNode parent = cursor.parent();
288 if (parent && parent.isG() && parent.hasId())
290 // alignment tab used for the first time inside a group
291 TNode row = doc.create("row");
292 TNode cell = doc.create("cell");
293 TNode g = doc.createG();
296 g.append(parent.first(), cursor);
298 else if (parent && parent.isG() && parent.parent().is("cell"))
300 // alignment tab used within a cell
301 TNode oldCell = parent.parent();
302 assert(oldCell && oldCell.is("cell"));
303 TNode row = oldCell.parent();
304 assert(row && row.is("row"));
305 TNode cell = doc.create("cell");
306 if (oldCell.next()) oldCell.next().insert(cell);
307 else row.append(cell);
308 TNode g = doc.createG();
314 logger.error("alignment tab used outside matrix");
319 TPushParser::do_eol()
321 //if (cursor.parent()) cursor.remove();
325 TPushParser::do_parameter(const std::string& p)
331 TPushParser::do_subscript()
333 TNode parent = cursor.parent();
336 TNode prev = cursor.prev();
339 TNode elem = doc.create("sb", nextId++);
340 TNode g = doc.createG();
341 cursor.replace(elem);
347 TNode elem = doc.create("sb", nextId++);
353 else if (parent.isSb() && cursor == parent[1])
355 if (parent["under"] == "1") logger.error("already under");
356 else parent["under"] = "1";
361 TPushParser::do_superscript()
363 TNode parent = cursor.parent();
366 TNode prev = cursor.prev();
369 TNode elem = doc.create("sp", nextId++);
370 TNode g = doc.createG();
371 cursor.replace(elem);
377 TNode elem = doc.create("sp", nextId++);
383 else if (parent.isSp() && cursor == parent[1])
385 if (parent["over"] == "1") logger.error("already over");
386 else parent["over"] = "1";
391 TPushParser::do_space(const std::string&)
393 logger.debug("do_space");
397 TPushParser::do_letter(const std::string& s)
399 //TNode parent = cursor.parent();
400 TNode elem = doc.createI(s, nextId++);
401 cursor.replace(elem);
406 TPushParser::do_digit(const std::string& s)
408 TNode parent = cursor.parent();
409 TNode prev = cursor.prev();
410 if (prev && parent.isG() && prev.is("n"))
412 TNode elem = doc.createN(prev.value() + s, nextId++);
417 TNode elem = doc.createN(s, nextId++);
418 cursor.replace(elem);
424 TPushParser::isPrimes(const TNode& node) const
427 return node.isG() && node.last() && node.last().is("o") && node.last()["val"] == PRIME();
431 TPushParser::do_apostrophe()
433 if (cursor.parent() && cursor.parent().isG())
435 if (TNode prev = cursor.prev())
437 if (prev.isSp() && prev[1] && isPrimes(prev[1]))
438 prev[1].append(doc.createO(PRIME(), nextId++));
439 else if (prev.isSb() && prev[0] &&
440 prev[0].isSp() && prev[0][1] &&
441 isPrimes(prev[0][1]))
442 prev[0][1].append(doc.createO(PRIME(), nextId++));
445 TNode elem = doc.create("sp");
446 TNode g = doc.createG();
450 g.append(doc.createO(PRIME(), nextId++));
456 logger.error("parser: you have to type one identifier before the ''");
466 TPushParser::do_other(const std::string& s)
474 /*cout << "TPushParser::do_other " << s << endl;
475 cout << "DOCUMENT: " << static_cast<GdomeNode*>(cursor.element().get_ownerDocument()) << endl;*/
476 TNode elem = doc.createT("o", s, nextId++);
477 cursor.replace(elem);
484 TPushParser::do_active(const std::string&)
490 TPushParser::do_comment()
498 TNode parent = cursor.parent();
499 if (parent && parent.isG() &&
500 parent.parent() && parent.parent().is("cell") &&
501 parent.parent().parent() && parent.parent().parent().is("row"))
503 TNode oldRow = parent.parent().parent();
505 TNode table = oldRow.parent();
507 TNode row = doc.create("row");
508 TNode cell = doc.create("cell");
509 TNode g = doc.createG();
510 if (oldRow.next()) oldRow.next().insert(row);
511 else table.append(row);
519 TPushParser::do_control(const std::string& name)
521 if (name == "cr") do_cr();
524 TNode parent = cursor.parent();
525 const TDictionary::Entry& entry = dictionary.find(name);
528 case TDictionary::IDENTIFIER:
530 TNode t = doc.createI(entry.value, nextId++);
536 case TDictionary::OPERATOR:
538 TNode t = doc.createO(entry.value, nextId++);
544 case TDictionary::NUMBER:
546 TNode t = doc.createN(entry.value, nextId++);
552 case TDictionary::MACRO:
554 TNode m = doc.createC(name, nextId++);
557 if (entry.leftOpen && entry.rightOpen)
559 assert(entry.pattern.empty());
560 assert(parent.isG());
561 TNode g1 = doc.createG();
562 g1["left-open"] = "1";
563 g1.append(parent.first(), m);
565 TNode g2 = doc.createG();
568 frames.push(Frame(entry));
570 else if (entry.leftOpen)
572 assert(parent.isG());
573 TNode g = doc.createG();
574 g["left-open"] = "1";
575 g.append(parent.first(), m);
579 else if (entry.rightOpen)
581 assert(entry.pattern.empty());
582 assert(parent.isG());
583 TNode g = doc.createG();
586 frames.push(Frame(entry));
588 else if (!entry.pattern.empty())
592 frames.push(Frame(entry));
594 if (entry.paramDelimited(0))
596 TNode g = doc.createG();
605 // error, but we could handle this very easily
606 logger.error(" parser:but we could handle this easily");
612 case TDictionary::UNDEFINED:
614 logger.error("parser: using undefined macro " + name);
615 TNode m = doc.createC(name, nextId++);
627 TPushParser::gdelete_prev_token()
629 assert(cursor.prev());
630 assert(cursor.parent());
631 TNode prev = cursor.prev();
632 assert(prev.is("i") || prev.is("o") || prev.is("n"));
634 // the control below is designed to handle the case in which val have more than one unicode character
635 DOM::UCS4String ucs4val(prev.element().getAttribute("val"));
636 if ((prev.is("i")) || (ucs4val.length() <= 1) || prev.element().hasAttribute("name"))
639 prev.replace(cursor);
641 if (cursor.parent().isC())
643 // in this case we have removed an element of a MACRO.
644 // we can assert that this element was a non delimited argument
645 assert(!frames.empty());
646 Frame& frame = frames.top();
647 assert(frame.pos > 0);
654 ucs4val.erase(ucs4val.length() - 1, 1);
655 prev.element().setAttribute(DOM::GdomeString("val"), DOM::GdomeString(ucs4val));
661 TPushParser::gdelete_prev_script()
663 // this method deletes an sp or an sb preceding the cursor
664 assert(cursor.prev());
665 assert(cursor.parent());
666 TNode prev = cursor.prev();
667 assert(prev.is("sp") || prev.is("sb"));
670 // we can invoke the gdelete_prev, because a sp (sb) MUST have two children
675 TPushParser::gdelete_prev_group()
677 assert(cursor.prev() && cursor.prev().isG());
678 TNode prev = cursor.prev();
682 // a group could have no children, so the gdelete_prev is not appropriate
683 // so, this method is not equivalent to the one above
688 TPushParser::gdelete_prev_macro()
690 assert(cursor.parent());
691 assert(cursor.prev());
692 TNode prev = cursor.prev();
695 const TDictionary::Entry& entry = dictionary.find(prev["name"]);
697 if (!entry.defined())
699 // We can assume that the user want to completely delete the undefined macro
701 prev.replace(cursor);
705 // we start to remove a MACRO. Different actions must be taken, based on the nature
706 // of the MACRO. In some cases, we can't remove the MACRO immediately, in other
707 // cases it's correct. In the first set of cases, we have to update the stack, pushing
708 // a frame in it with a correct value of pos, in the
709 // second one, we must not push a frame in the stack
713 // In this fragment of code we also handle the leftOpen && rightOpen MACRO.
714 // if the control element is rightOpen, the cursor should be placed after
715 // the last child of the control element's last child, and than, we try to remove something.
716 // A frame MUST be pushed in the stack, because we dont' know if the following actions
717 // will completely remove the MACRO.
718 frames.push(Frame(entry));
720 // Since the MACRO is rightOpen, the last child of the MACRO must be a phantom group
721 assert(prev.last().isG() && !prev.last().hasId());
724 prev.last().append(cursor);
726 // the gdelete_prev is not appropriate, because the last child of the MACRO could have no children
727 do_gdelete_phantom_group();
729 else if (entry.leftOpen)
731 // the leftOpen MACRO MUST have one and only one child, which MUST be a phantom group
732 // In this case, we do not have to push a frame in the stack, because we remove the
733 // MACRO immediately, substituting it with the content of the phantom group.
734 // At the moment, we don't remove the last child of the phantom group, but
735 // it's not clear if it's the correct behavior of the graphical deleting.
736 // To delete it, just remove the comment of the last instruction of the
737 // if (g.size()) block.
738 assert(prev.first());
739 assert(prev.first().isG());
740 assert(prev.first() == prev.last());
742 TNode g = prev.first();
745 // in this case, the phantom group has at least one child, so we can call the
748 prev.replace(g.first(), TNode());
753 // otherwise, the phantom group has no children, so we remove it, also the MACRO.
756 prev.replace(cursor);
759 else if (!entry.pattern.empty())
761 // we have to start to remove a MACRO which accepts arguments.
762 // If the MACRO accepts arguments, the MACRO has at least one child
763 assert(prev.size() >= 1);
765 // Differnt actions must be taken, based on the nature of the last child
766 // of the MACRO. We have to distinguish the case in which it's a delimited argument,
767 // frome the one in which it's a not delimited argument.
768 if (prev.last().isG() && !prev.last().hasId())
770 // the last argument of the MACRO is a delimited argumet. We ideally remove
771 // the sequence of delimiters
773 prev.last().append(cursor);
774 // we have to push a frame with a correct value of pos
775 assert(entry.previousParam(entry.pattern.size()) != entry.pattern.size());
777 unsigned sequence_length = entry.pattern.size() - entry.previousParam(entry.pattern.size()) - 1;
778 unsigned p = entry.pattern.size() - sequence_length - 1;
779 // now, p is the correct value of pos, and we can push the frame.
780 frames.push(Frame(entry, p));
784 // in this case, the last child of the MACRO is not a delimited argument, so we try
785 // to remove it, but we have to take differnt actions if the MACRO is a table with rows or not.
787 if (entry.table == 1 && prev.last().is("row"))
789 // in this case the cursor should be appended to the group associated to
790 // the last cell of the last row of the table
791 assert(prev.last().last().is("cell") && prev.last().last().first().isG());
792 prev.last().last().first().append(cursor);
799 // we push a frame in the stack with a correct value of member pos.
800 // This correct value is the size of the pattern, because we have to START to delete
801 // a MACRO. It means that all of the MACRO's arguments have been inserted.
802 frames.push(Frame(entry, entry.pattern.size()));
806 // in this case we try to remove the last child of the MACRO.
811 // this block of code will never be executed, because the MACRO accepts arguments,
812 // so a MACRO's child MUST exist. But, we can handle this case, emitting a warning
813 logger.warning("Parser: the TML tree is in a strange state, but a good state will be generated");
816 } // end of the else of the if (prev.last().isG() && !prev.last().hasId())
818 } // end of if (!entry.pattern.empty())
821 // if we are here, the MACRO preceding the cursor, is !(rightOpen || leftOpen),
822 // and has no pattern. It means that it has no children.
823 // We can replace it with the cursor
824 assert(prev.size() == 0);
826 prev.replace(cursor);
828 } // end of defined MACRO
833 TPushParser::gdelete_prev()
835 // if in this function, the prev of cursor does exist, also the parent and we want a graphical deleting.
837 assert(cursor.prev());
838 assert(cursor.parent());
840 TNode prev = cursor.prev();
842 if (prev.is("i") || prev.is("o") || prev.is("n"))
844 gdelete_prev_token();
846 else if (prev.isSp() || prev.isSb())
848 gdelete_prev_script();
852 gdelete_prev_group();
856 // here, we also treat the case in which the MACRO is a table
857 gdelete_prev_macro();
861 // not handled. Future cases...
867 TPushParser::rgreplace_father(void)
869 // this method MUST only be invoked, when the cursor
870 // is the only child of a group with id. This function
871 // replaces the group with the cursor. But if the new parent
872 // is a group with id and the cursor is the only child of the
873 // group, the new parent is replaced...and so on.
874 // r stands for recursive, g stands for graphical.
875 assert(cursor.parent());
876 assert(cursor.parent().isG() && cursor.parent().hasId());
878 TNode parent = cursor.parent();
880 while (parent.isG() && parent.hasId() && (parent.first() == cursor))
882 parent.replace(cursor);
883 parent = cursor.parent();
888 // in this case we have removed a MACRO's child.
889 // So, we have to update the member pos of the frame in the stack
890 // We can assert that this MACRO accepts arguments.
891 assert(!frames.empty());
892 Frame& frame = frames.top();
893 assert(frame.pos > 0);
899 TPushParser::do_gdelete_script()
901 // If we are here, the cursor is child of a script (sp or sb) and
902 // this means that a prev does exist and that there is one and only one
903 // element preceding the cursor. The sp's (or sb's) parent
904 // MUST NOT be a MACRO.
905 // The element preceding the cursor is the base of the script.
907 assert(cursor.parent() && (cursor.parent().isSp() || cursor.parent().isSb()));
908 TNode parent = cursor.parent();
910 assert(parent.size() == 2);
911 assert(parent.parent() && !parent.parent().isC());
913 TNode prev = cursor.prev();
915 if (prev.isG() /*&& !prev.hasId()*/ && (prev.size() == 0))
917 // in this case, the script's base is a group with no elements, so
918 // we have to remove the entire MACRO, replacing it with the cursor.
919 // This situation occurs when the user type something like this
926 parent.replace(cursor);
928 // if the new parent is a group with Id and the cursor is the only
929 // element of this group, we have to remove the group. These controls are made
930 // in the method rgreplace_father().
931 if (cursor.parent().isG() && cursor.parent().hasId()) rgreplace_father();
935 // in this case, the prev has to replace the script,
936 // and the cursor has to be placed after the prev.
937 assert(prev.hasId());
938 parent.replace(prev);
939 prev.parent().append(cursor);
940 // now prev have a preceding element
941 assert(cursor.parent().size() > 1);
944 } // end of method do_gdelete_script
947 TPushParser::do_gdelete_macro()
949 // If we are here, the cursor is a child of a MACRO and this means
950 // that there is an open frame for the control element
951 // and this element is closed at either side (no leftOpen no rightOpen)
952 // and the MACRO is waiting for a not delimited argument, so
953 // we can assert that frame.entry.pattern.size() >= 1
954 assert(cursor.parent() && cursor.parent().isC());
955 TNode parent = cursor.parent();
957 assert(!frames.empty());
958 Frame& frame = frames.top();
959 assert(frame.entry.pattern.size() >= 1);
961 // we have to take different actions, based on if a preceding element exists
963 TNode prev = cursor.prev();
966 // in this case, a prev does not exist, so the actions of deleting means
967 // that we have to remove the MACRO. So we have to pop the stack.
968 // Being here also means that the MACRO is waiting for the first argument
969 // (which is not delimited), but we don't mind about it.
970 assert(frame.pos == 0);
971 parent.replace(cursor);
974 // if the new parent is a group with Id, and has no elements other than the
975 // cursor, we can remove it, because it' a graphical deleting
976 if (cursor.parent() && cursor.parent().isG() && cursor.parent().hasId())
978 else if (cursor.parent().isC())
980 // We have assumed that a MACRO cannot be a MACRO's child.
981 // At the moment, this assumption is valid, but in a future
982 // it might be false.
983 assert(!frames.empty());
984 Frame& frame = frames.top();
985 assert(frame.pos > 0);
991 // a prev does exist, we have to control if it's a delimited argument or not.
992 if (prev.isG() && !prev.hasId())
994 // in this case, prev is a delimited argument, so we have
995 // to ideally remove the sequence of delimiters
996 Frame& frame = frames.top();
997 assert(frame.pos > 1);
1000 assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
1002 // these 3 lines of code update the member pos.
1003 unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
1004 assert(sequence_length);
1005 frame.pos = frame.pos - sequence_length - 1;
1009 // the prev is not a delimited argument, so we have to try to remove it.
1010 // We "try", because the prev might be a something that
1011 // a simple delete cannot remove completely
1019 TPushParser::do_gdelete_groupId()
1021 // if we are here, the cursor's parent is a group with Id
1022 assert(cursor.parent() && cursor.parent().isG() && cursor.parent().hasId());
1023 TNode parent = cursor.parent();
1025 // we have to take different actions based on if the cursor has a preceding
1027 TNode prev = cursor.prev();
1030 // the cursor has a preceding element, so we try to remove it
1033 // We control if the group has to be removed, because the cursor
1034 // might be the only element of the group.
1035 if ((parent.first() == cursor) && parent.isG() && parent.hasId())
1041 // the cursor has no preceding elements, so we have to remove the
1045 // we have to re-start the process, because it' a graphical delete
1049 } // end of method do_gdelete_groupId()
1052 TPushParser::do_gdelete_phantom_group()
1054 // if we are here, the cursor MUST be a child of a
1056 assert(cursor.parent() && cursor.parent().isG() && !cursor.parent().hasId());
1058 TNode parent = cursor.parent();
1060 // now we have to control if the cursor has a preceding element or not
1061 TNode prev = cursor.prev();
1064 // the cursor has a preceding element, so we try to remove it
1067 if (parent.size() == 1 && parent.parent().isSp())
1069 // in this case the gdelete_prev has removed the only element preceding the cursor.
1070 // If the phantom group is an sp's child, it means that the user has removed all \' in the
1072 // We can remove the phamtom group and the sp element. But we also can only remove the
1073 // phantom group, giving the user the possibility of inserting an exponent.
1074 // At the moment, we remove the sp element (and the phantom group), because it may be the correct
1075 // behavior of a graphical deleting.
1077 parent.replace(cursor);
1078 // now we have an sp element with two children: the first child (we don't know anything about it)
1080 assert(cursor.parent().size() == 2);
1082 // to delete the script we can invoke the do_gdelete_script(), which will do all controls we need.
1083 // To give the possibility of insetring an exponent, just remove the following istruction.
1084 do_gdelete_script();
1086 else if (parent.parent().isSp())
1088 // in this case we have to place the cursor after the sp element
1090 assert(parent.parent().parent());
1091 parent.parent().parent().append(cursor);
1096 // in this case the cursor is the only element of the phantom group,
1097 // so we have to remove it. But, a phantom group has a special role,
1098 // so we have to control the grand father of the cursor.
1099 TNode gfather = parent.parent();
1102 // If here, the TML tree is in an inconsistent state
1103 logger.error("do_gdelete_phantom: TML tree in a inconsistent state");
1105 else if (gfather.isC())
1107 // in this case the phantom group is child of a MACRO.
1108 // We have to control the nature of this MACRO.
1109 assert(!frames.empty());
1110 Frame& frame = frames.top();
1112 if (frame.entry.leftOpen && frame.entry.rightOpen)
1114 // in this case, the cursor'parent is in the second and last child
1115 // of the MACRO. We can assert that the grand father has two
1116 // children, which are both phantom groups
1117 assert(gfather.size() == 2);
1118 assert((gfather.last() == parent) && (gfather.first().isG() && !gfather.first().hasId()));
1119 assert(frame.pos == 0);
1121 TNode ggfather = gfather.parent();
1125 // we have to replace the gfather with the elements of its first child, but this group may have no
1127 if (gfather.first().size())
1129 gfather.replace(gfather.first().first(), TNode());
1130 ggfather.append(cursor);
1134 // in this case, the MACRO has to be replaced with the cursor
1135 gfather.first().remove();
1136 gfather.replace(cursor);
1138 // now we have the situation preceding the insertion of the leftOpen and rightOpen MACRO.
1139 // this MACRO no longer exists.
1142 else if (frame.entry.rightOpen)
1144 // the user has inserted a rightOpen MACRO, and now, this MACRO has no children (excluding the
1145 // phantom group), so we remove the MACRO.
1146 // We can assert that cursor's parent is the only child of the MACRO
1147 assert(gfather.size() == 1);
1148 assert(frame.pos == 0);
1151 gfather.replace(cursor);
1153 // now we have the situation preceding the rightOpen MACRO, so we have to pop the frame
1156 else if (frame.entry.leftOpen)
1158 // this situation will never occur.
1159 logger.error("the parser has generated a wrong TML tree");
1161 else if (!frame.entry.pattern.empty())
1163 // the MACRO accepts arguments, and the phantom group in which
1164 // the cursor is, rappresents a delimited argument
1165 // We have to control if the cursor's parent has a preceding element,
1167 TNode uncle = parent.prev();
1170 // the parent is the only element of the MACRO.
1171 // we can assert that frame.pos == 0.
1172 // In this case we can replace the MACRO with the cursor
1173 assert(frame.pos == 0);
1176 gfather.replace(cursor);
1181 // the parent has a preceding element. Now we have
1182 // to control if the uncle is a delimited argument or not.
1183 if (uncle.isG() && !uncle.hasId())
1185 // the uncle is a delimited argument. So we have to ideally
1186 // remove the sequence of delimiters.
1187 assert(frame.pos > 1);
1188 unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
1189 assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
1190 assert(sequence_length);
1191 // sequence_length is the length of the delimiters sequence which separates
1192 // the current parameter and the previous parameter
1193 frame.pos = frame.pos - sequence_length - 1;
1196 uncle.append(cursor);
1200 // the uncle is a not delimited argument, so we try to remove it.
1202 parent.replace(cursor);
1203 parent = cursor.parent(); // i update the parent (it should be the MACRO)
1204 assert(parent.isC());
1206 // now we try to remove the uncle (now it' the preceding element)
1209 } // this is the else's end, that handles the case in which an uncle exists
1210 } // end of if (!frame.entry.pattern.empty())
1213 // the entry has no arguments, is not rightOpen and is not leftOpen.
1214 logger.warning("parser: TML tree in a strange state, but we try to recover from it");
1215 if (gfather.size() == 1)
1219 gfather.replace(cursor);
1223 logger.warning("parser: TML tree in a very strange state, but we try to recover from it");
1225 gfather.replace(cursor);
1228 } // end of if (gfather.isC())
1229 else if (gfather.is("cell"))
1231 // A table is a control sequence, so there is a frame in the stack
1232 assert(!frames.empty());
1233 assert(frames.top().pos == 1);
1234 assert(frames.top().entry.table == 1);
1236 // a cell MUST be a row's child, which in turn is a table's child
1237 assert(gfather.parent() && gfather.parent().is("row") && gfather.parent().parent());
1238 TNode row = gfather.parent();
1240 // in this case the cell has no element, so the user wants to delete this cell.
1241 TNode prev_cell = gfather.prev();
1245 // now the cell no longer exists
1249 // in this case, the cell is the only cell in the row.
1250 // So, we assume that the user wants to delete the entire row.
1251 TNode table = row.parent();
1252 TNode prev_row = row.prev();
1257 // the row was the only child of the table.
1258 // so we have to delete the entire table
1259 assert(table.parent());
1260 TNode parent_table = table.parent();
1263 parent_table.append(cursor);
1267 // there are other rows (one or more)
1268 assert(prev_row.is("row"));
1269 assert(prev_row.last());
1270 TNode last_cell = prev_row.last();
1271 assert(last_cell.is("cell"));
1272 assert(last_cell.size() == 1);
1273 assert(last_cell.first().isG() && !last_cell.first().hasId());
1274 last_cell.first().append(cursor);
1276 } // end of if (!prev_cell)
1279 // being here means that there is a previous cell,
1280 // so we append the cursor to group.
1281 assert(prev_cell.size() == 1);
1282 assert(prev_cell.first().isG() && !prev_cell.first().hasId());
1283 prev_cell.first().append(cursor);
1285 } // end of if (gfather.is("cell"))
1286 else if (gfather.isSp())
1288 // in this case, the user typed a \'. So this phantom group
1289 // contained a sequence of \'.
1290 // Maybe in this part will never be used, because, if we delete last \' in the
1291 // phantom group, we remove the phantom group also
1293 // In any case, if we are here we have two possibilities:
1294 // we can delete the phantom group;
1295 // we can delete the superscript.
1296 // At the moment we implement the first solution. To implement the second one, just remove
1297 // the line code after the logger.warning and remove comments from the remaining lines
1298 logger.warning("parser: TML tree in a strange state, we try to recover from it");
1299 parent.replace(cursor);
1303 //gfather.replace(cursor);
1305 else if (gfather.is("math"))
1307 // in this case we ignore the user's will of deleting
1308 // but we can decide to remove the math mode.
1309 logger.info("Parser: nothing to delete");
1313 // cursor's grand father is undefined
1314 logger.error("parser: TML tree is in a unknown state");
1316 } // end of the else of the if (prev)
1322 TPushParser::do_gdelete()
1324 // we have to handle the case in wich the cursor has a parent or not
1325 if (!cursor.parent())
1327 // it's not a good situation...at the moment we do not take actions
1328 logger.error("TML tree not well structured");
1332 // a parent exists. We have to take differnt actions, based on the nature of
1334 TNode parent = cursor.parent();
1335 if (parent.is("math"))
1337 // we ca do two thing...we can remove the math mode (it implies controlling the display attribute), we can do nothing
1338 // At the moment, the user's will of deleting is simply ignored
1339 logger.warning("nothing to delete");
1341 else if (parent.isG())
1343 // the cursor's parent is a group. We have to control if it's a phantom group or not
1346 do_gdelete_groupId();
1350 do_gdelete_phantom_group();
1352 } // end of parent is group
1353 else if (parent.isC())
1356 } // end of parent is a MACRO
1357 else if (parent.isSp() || parent.isSb())
1359 do_gdelete_script();
1360 } // end of parent is sp or sb
1361 } // end of the else which consider the case in which parent exists
1363 } // end of method do_gdelete
1366 TPushParser::process(const TToken& token)
1368 switch (token.category)
1370 case TToken::BEGIN: do_begin(); break;
1371 case TToken::END: do_end(); break;
1372 case TToken::SHIFT: do_shift(); break;
1373 case TToken::ALIGN: do_align(); break;
1374 case TToken::EOL: do_eol(); break;
1375 case TToken::PARAMETER: do_parameter(token.value); break;
1376 case TToken::SUPERSCRIPT: do_superscript(); break;
1377 case TToken::SUBSCRIPT: do_subscript(); break;
1378 case TToken::SPACE: do_space(token.value); break;
1379 case TToken::LETTER: do_letter(token.value); break;
1380 case TToken::DIGIT: do_digit(token.value); break;
1381 case TToken::OTHER: do_other(token.value); break;
1382 case TToken::ACTIVE: do_active(token.value); break;
1383 case TToken::COMMENT: do_comment(); break;
1384 case TToken::CONTROL: do_control(token.value); break;
1389 TPushParser::push(const TToken& token)
1391 TNode parent = cursor.parent();
1392 // If the cursor has no parent then it is detached from the editing
1393 // tree, which means this token will be ignored
1396 // If the parent is a phantom group and the grand-parent is a
1397 // control sequence, there are two cases:
1398 // a. we are parsing a delimited argument of a entry
1399 // b. we are parsing a side of a right- or left-open entry
1400 if (parent.isG() && !parent.hasId() && parent.parent().isC())
1402 // There must be an open frame, for the grand-parent is a control sequence
1403 assert(!frames.empty());
1404 Frame& frame = frames.top();
1405 if (!frame.entry.pattern.empty())
1407 // The entry pattern is not empty. By our conventions this means
1408 // the entry cannot be open at either end, hence we are parsing
1409 // a delimited argument
1410 assert(frame.pos + 1 < frame.entry.pattern.size());
1411 assert(frame.entry.pattern[frame.pos + 1].category != TToken::PARAMETER);
1412 if (frame.entry.pattern[frame.pos + 1] == token)
1414 // The token matches with a delimiter of the argument,
1415 // hence we increment the frame.pos
1418 if (frame.entry.lastDelimiter(frame.pos))
1420 // this delimiter is the last one for the argumet,
1421 // so the argument is completed
1425 if (frame.pos == frame.entry.pattern.size())
1427 // This token has completed the entry
1430 else if (frame.entry.paramDelimited(frame.pos))
1432 // For the next is a delimited argument we have to place
1433 // a suitable phantom group with the cursor inside
1434 TNode g = doc.createG();
1435 parent.parent().append(g);
1439 parent.parent().append(cursor);
1444 // Delimiter mismatch.
1445 if (frame.entry.pattern[frame.pos].category != TToken::PARAMETER)
1447 // in this case, there is a sequence of delimiters that delimitates
1448 // the argument, and the user has correctly inserted a portion of this
1449 // sequence, but now has inserted a wrong delimiter.
1450 // Here, there are some possibilities:
1451 // - ignore the token, and wait for the correct delimiter
1452 // - ignore the token, wait for the correct delimiter and emit an error
1453 // At the moment, we implement the second possibily
1454 logger.error("parser: it's not the correct delimiter...you have to type " + frame.entry.pattern[frame.pos + 1].value);
1458 // in this case, the sequence of delimiters is composed of one
1459 // delimiter. It means that we have to process the token
1466 // The entry pattern is empty, hence we are parsing a right-open
1467 // entry. What happens if we actually are in the left side?
1468 // This could happen only when re-editing an entered expression
1470 assert(frame.entry.rightOpen);
1474 else if (parent.isC())
1476 // We are parsing a non-delimited argument entry
1478 Frame& frame = frames.top();
1479 assert(frame.pos < frame.entry.pattern.size());
1481 if (frame.entry.pattern[frame.pos].category == TToken::PARAMETER)
1483 // As by the TeX parsing rules of undelimited parameters,
1484 // empty spaces are ignored
1485 if (token.category != TToken::SPACE)
1487 // We need to increase the frame position here, becase inside
1488 // process the function advance will be called. At that point
1489 // it will be important for the parser to know that the entry
1490 // has been completed in order to place the cursor correctly
1491 // in the next position
1496 else if (frame.entry.pattern[frame.pos] == token)
1498 // The token has been accepted
1500 if (frame.pos < frame.entry.pattern.size() &&
1501 frame.entry.paramDelimited(frame.pos))
1503 // If the next is a delimited argument we have to place
1504 // the phantom group with the cursor inside
1505 TNode g = doc.createG();
1514 // There is a mismatch. Emit an error and ignore the token?
1515 logger.debug("parser: token ignored: " + token.value);
1523 logger.warning("ignored token");
1526 if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1533 if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1539 TPushParser::advance(const TNode& node)
1546 logger.error("wrong TML tree");
1548 else if (node.parent().isG())
1550 TNode next = node.next();
1551 if (next) next.insert(cursor);
1552 else node.parent().append(cursor);
1554 else if (node.parent().isC())
1556 assert(!frames.empty());
1557 if (frames.top().pos == frames.top().entry.pattern.size())
1559 if (frames.top().entry.rightOpen)
1561 // we have to remove the frame from the stack
1563 advance(node.parent().parent());
1568 advance(node.parent());
1571 else if (frames.top().entry.paramDelimited(frames.top().pos))
1573 // the next argument is delimited, so we have to create a phantom group
1574 TNode g = doc.createG();
1576 node.parent().append(g);
1580 // the next argumet is not delimited, so we have to append the cursor
1582 node.parent().append(cursor);
1585 else advance(node.parent());
1590 * This version handles the case in which we have to
1591 * create a delimited argument
1594 TPushParser::advance(const TNode& node)
1597 TNode parent = node.parent();
1599 ; // nothing to do, the cursor is not in the document any more
1600 else if (parent.isG())
1602 TNode next = node.next();
1603 if (next) next.insert(cursor);
1604 else parent.append(cursor);
1606 else if (parent.isC())
1612 Frame& frame = frames.top();
1613 if (frame.pos == frame.entry.pattern.size())
1618 else if (frame.entry.paramDelimited(frame.pos))
1620 // the next argument is delimited, so we have to create a phantom group
1621 // with the cursor inside. We have to remember that, since we are here,
1622 // the cursor has been removed
1623 TNode g = doc.createG();
1629 // otherwise, the next MACRO's argument is not delimited, so we just
1630 // append the cursor to the MACRO
1631 parent.append(cursor);
1635 else if (parent.is("math"))
1645 TPushParser::advance(const TNode& node)
1648 TNode parent = node.parent();
1650 ; // nothing to do, the cursor is not in the document any more
1651 else if (parent.isG())
1653 TNode next = node.next();
1654 if (next) next.insert(cursor);
1655 else parent.append(cursor);
1657 else if (parent.isC())
1663 Frame& frame = frames.top();
1664 if (frame.pos == frame.entry.pattern.size())
1670 parent.append(cursor);
1673 else if (parent.is("math"))
1682 TPushParser::setCursorHint(const std::string& c)
1684 if (cursor["val"] != c)
1687 if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1694 if (APushParser::thaw() && factory && doc.dirtyNode())
1696 factory->documentModified(doc);