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);
42 const TDictionary::Entry entry = dictionary.find("prime");
43 if (entry.cls == TDictionary::OPERATOR)
45 TNode op = doc.createO(entry.value, nextId++);
51 TNode op = doc.createO("?", nextId++);
57 TPushParser::do_begin()
59 TNode parent = cursor.parent();
60 if (parent.isC() && dictionary.find(parent.nameC()).table)
62 TNode row = doc.create("row");
63 TNode cell = doc.create("cell");
64 TNode g = doc.createG();
72 TNode g = doc.createG(nextId++);
80 TPushParser::correctBrace()
82 // this method MUST be invoked when the cursor is child of a
83 // phantom group, which in turn is the last rightOpen MACRO's child.
84 // The only way to exit from a rightOpen MACRO is opening a group before
85 // inserting the MACRO and, once the MACRO is completely inserted, closing
87 // This method return true if the condition above is true. False, otherwise.
88 assert(cursor.parent() && cursor.parent().isG() && !cursor.parent().hasId());
89 TNode parent = cursor.parent();
90 assert(parent.parent() && parent.parent().isC());
91 assert(!frames.empty());
92 Frame& frame = frames.top();
93 assert(frame.entry.rightOpen);
94 assert(parent.parent().last() == parent);
96 TNode c = parent.parent();
99 TNode node = c.parent();
102 if (node.isG() && node.hasId())
104 // in this case, the rightOpen MACRO is a child of a group with id.
105 // So, the '}' is correct
111 // the MACRO is a phantom group's child. We have to control why we
112 // have this phantom group
113 TNode nodeParent = node.parent();
114 if (nodeParent && nodeParent.isC())
116 // we have to control the nature of this MACRO
117 const TDictionary::Entry& entry = dictionary.find(nodeParent.nameC());
118 if (entry.rightOpen && node == nodeParent.last())
120 // in this case we have to re-iterate the process
121 node = nodeParent.parent();
129 // at the moment we assume that a MACRO cannot be child of an element other than a group
139 TPushParser::do_end()
141 TNode parent = cursor.parent();
142 if (parent && parent.isG() && parent.hasId())
144 // normal closing brace for an explicitly open group
149 else if (parent && parent.isG() && parent.parent() && parent.parent().is("cell"))
151 assert(!frames.empty());
152 // closing brace for a structure in which & or \cr have been used
153 TNode row = parent.parent().parent();
154 assert(row && row.is("row"));
155 assert(row.parent());
159 else if (parent && parent.isG() && !parent.hasId() && parent.parent() && !parent.parent().is("math"))
161 // In this case, we have to control the cursor's grand parent.
162 TNode gparent = parent.parent();
164 if (gparent.isC() && gparent.last() == parent)
166 // a frame MUST be in the stack
167 assert(!frames.empty());
169 // we have to control the nature of this macro
170 if (frames.top().entry.rightOpen)
172 // in this case, the '}' character is the proper way to exit from the phantom group, and
173 // in particular, this character means that the user wants to exit from the MACRO.
174 // A rightOpen MACRO MUST be descendant of a group with Id. This '}' is the closing brace of this
175 // group. So, we have to control if this group exists. This groyp could exist, but this MACRO could
176 // be another MACRO's child, so we have to control this last MACRO recursively. This recurive control
177 // is done by the correctBrace method.
180 // the '}' is not correct
181 logger.warning("nothing to close");
193 logger.warning("closing brace ignored");
199 // at the moment, a phantom group with the cursor inside can be a MACRO's child or a cell's child, and these cases
200 // are handled in other blocks of code.
201 logger.error("do_end: strange TML tree");
207 // In this case, there is a redundant '}', so we can ignore it and
209 logger.warning("There is so no corresponding'{'");
216 TPushParser::do_shift()
218 TNode parent = cursor.parent();
220 if (parent.is("tex"))
222 TNode math = doc.create("math", nextId++);
223 TNode g = doc.createG();
224 cursor.replace(math);
229 else if (parent.isG() && !parent.hasId() && parent.parent() && parent.parent().is("math"))
233 // there is something before the cursor, hence this is the
234 // closing math shift
235 if (parent.parent()["display"] != "1")
237 // one math shift is enough to close it
243 // we need two closing math shifts
244 //cursor.remove(); ??
245 parent.parent().append(cursor);
249 else if (parent.parent()["display"] != "1")
251 // there is nothing before the cursor, and the math is not
252 // in display mode, so this must be a double math shift
253 parent.parent()["display"] = "1";
258 parent.parent().append(cursor);
262 else if (parent.is("math"))
269 logger.warning("parser: math shift");
275 TPushParser::do_align()
277 TNode parent = cursor.parent();
278 if (parent && parent.isG() && parent.hasId())
280 // alignment tab used for the first time inside a group
281 TNode row = doc.create("row");
282 TNode cell = doc.create("cell");
283 TNode g = doc.createG();
286 g.append(parent.first(), cursor);
289 else if (parent && parent.isG() && parent.parent().is("cell"))
291 // alignment tab used within a cell
292 TNode oldCell = parent.parent();
293 assert(oldCell && oldCell.is("cell"));
294 TNode row = oldCell.parent();
295 assert(row && row.is("row"));
296 TNode cell = doc.create("cell");
297 if (oldCell.next()) oldCell.next().insert(cell);
298 else row.append(cell);
299 TNode g = doc.createG();
306 logger.warning("alignment tab used outside matrix");
312 TPushParser::do_eol()
314 //if (cursor.parent()) cursor.remove();
315 logger.warning("token ignored");
320 TPushParser::do_parameter(const std::string& p)
322 logger.warning("token ignored");
327 TPushParser::do_subscript()
329 TNode parent = cursor.parent();
332 TNode prev = cursor.prev();
335 TNode elem = doc.create("sb", nextId++);
336 TNode g = doc.createG();
337 cursor.replace(elem);
344 TNode elem = doc.create("sb", nextId++);
351 else if (parent.isSb() && cursor == parent[1])
353 if (parent["under"] == "1")
355 logger.warning("already under");
360 parent["under"] = "1";
366 logger.warning("token ignored");
372 TPushParser::do_superscript()
374 TNode parent = cursor.parent();
377 TNode prev = cursor.prev();
380 TNode elem = doc.create("sp", nextId++);
381 TNode g = doc.createG();
382 cursor.replace(elem);
389 TNode elem = doc.create("sp", nextId++);
396 else if (parent.isSp() && cursor == parent[1])
398 if (parent["over"] == "1")
400 logger.warning("token ignored: already over");
405 parent["over"] = "1";
411 logger.warning("token ignored");
417 TPushParser::do_space(const std::string&)
423 TPushParser::do_letter(const std::string& s)
425 //TNode parent = cursor.parent();
426 TNode elem = doc.createI(s, nextId++);
427 cursor.replace(elem);
433 TPushParser::do_digit(const std::string& s)
435 TNode elem = doc.createN(s, nextId++);
436 cursor.replace(elem);
442 TPushParser::isPrimes(const TNode& node) const
445 return node.isG() && node.last() && node.last().is("o") && node.last()["name"] == "prime";
449 TPushParser::do_apostrophe()
451 if (cursor.parent() && cursor.parent().isG())
453 if (TNode prev = cursor.prev())
455 if (prev.isSp() && prev[1] && isPrimes(prev[1]))
457 prev[1].append(PRIME());
460 else if (prev.isSb() && prev[0] &&
461 prev[0].isSp() && prev[0][1] &&
462 isPrimes(prev[0][1]))
464 prev[0][1].append(PRIME());
469 TNode elem = doc.create("sp");
470 TNode g = doc.createG();
481 logger.warning("parser: you have to type an identifier before ''");
487 logger.warning("token ignored: you have to be in a group");
493 TPushParser::do_other(const std::string& s)
498 return do_apostrophe();
501 /*cout << "TPushParser::do_other " << s << endl;
502 cout << "DOCUMENT: " << static_cast<GdomeNode*>(cursor.element().get_ownerDocument()) << endl;*/
503 TNode elem = doc.createT("o", s, nextId++);
504 cursor.replace(elem);
512 TPushParser::do_active(const std::string&)
515 logger.warning("token ignorde");
520 TPushParser::do_comment()
529 TNode parent = cursor.parent();
530 if (parent && parent.isG() &&
531 parent.parent() && parent.parent().is("cell") &&
532 parent.parent().parent() && parent.parent().parent().is("row"))
534 TNode oldRow = parent.parent().parent();
536 TNode table = oldRow.parent();
538 TNode row = doc.create("row");
539 TNode cell = doc.create("cell");
540 TNode g = doc.createG();
541 if (oldRow.next()) oldRow.next().insert(row);
542 else table.append(row);
550 // at the moment, \cr can only be used inside a table
551 logger.warning("token ignored: cr used outside a table");
557 TPushParser::do_control(const std::string& name)
559 if (name == "cr") return do_cr();
562 TNode parent = cursor.parent();
563 const TDictionary::Entry& entry = dictionary.find(name);
566 case TDictionary::IDENTIFIER:
568 TNode t = doc.createI(entry.value, nextId++);
575 case TDictionary::OPERATOR:
577 TNode t = doc.createO(entry.value, nextId++);
584 case TDictionary::NUMBER:
586 TNode t = doc.createN(entry.value, nextId++);
593 case TDictionary::MACRO:
597 TNode m = doc.createC(name, nextId++);
599 if (entry.leftOpen && entry.rightOpen)
601 assert(entry.pattern.empty());
602 assert(parent.isG());
603 TNode g1 = doc.createG();
604 g1["left-open"] = "1";
605 g1.append(parent.first(), m);
607 TNode g2 = doc.createG();
610 frames.push(Frame(entry));
612 else if (entry.leftOpen)
614 assert(parent.isG());
615 TNode g = doc.createG();
616 g["left-open"] = "1";
617 g.append(parent.first(), m);
621 else if (entry.rightOpen)
623 assert(entry.pattern.empty());
624 assert(parent.isG());
625 TNode g = doc.createG();
628 frames.push(Frame(entry));
630 else if (!entry.pattern.empty())
632 frames.push(Frame(entry));
633 if (entry.paramDelimited(0))
635 TNode g = doc.createG();
644 // it's an empty macro
649 else if (!entry.pattern.size() && !entry.rightOpen && !entry.leftOpen)
651 // a macro with no arguments and no right open and no left open, can be child of anything
652 TNode m = doc.createC(name, nextId++);
659 // a macro with arguments or a rightOpen or leftOpen macro must be a group's child
660 logger.warning("token ignored: this macro should be in a group");
665 case TDictionary::UNDEFINED:
667 logger.warning("parser: using undefined macro " + name);
668 TNode m = doc.createC(name, nextId++);
677 logger.warning("token ignored");
685 TPushParser::drop_prev_token(bool special)
687 assert(cursor.prev());
688 assert(cursor.parent());
689 TNode prev = cursor.prev();
690 assert(prev.is("i") || prev.is("o") || prev.is("n"));
692 DOM::UCS4String ucs4val = prev.element().getAttribute("val");
693 bool macro = prev.element().hasAttribute("name");
694 std::string utf8name;
695 if (macro) utf8name = prev.element().getAttribute("name");
698 prev.replace(cursor);
700 if (cursor.parent().isC())
702 // in this case we have removed an element of a MACRO.
703 // we can assert that this element was a non delimited argument
704 assert(!frames.empty());
705 Frame& frame = frames.top();
706 assert(frame.pos > 0);
710 if ((ucs4val.length() > 1) && !special)
714 // in this case we can return the content of ucs4val, but we have
715 // to convert it in a utf8
716 DOM::GdomeString gdsval(ucs4val);
717 std::string utf8val(gdsval);
718 utf8val = utf8val.erase(utf8val.length() - 1, 1);
719 return std::string(utf8val);
723 // in this case, the content of val could be in unicode,
724 // but we have attribute name, which doesn't contain character not representable
726 return "\\" + utf8name.erase(utf8name.length() - 1, 1);
729 else if ((ucs4val.length() <= 1) && macro && special) return "\\" + utf8name.erase(utf8name.length() - 1, 1);
734 TPushParser::drop_prev_script(bool special)
736 // this method deletes an sp or an sb preceding the cursor
737 assert(cursor.prev());
738 assert(cursor.parent());
739 TNode prev = cursor.prev();
740 assert(prev.is("sp") || prev.is("sb"));
743 // we can invoke the drop_prev, because a sp (sb) MUST have two children
744 // but we cannot invoke do_drop_script because it assumes when called, the first
745 // child has been removed yet.
746 if (cursor.prev().isG() && !prev.hasId())
748 // in this case, the user has inserted the a sequence of '.
749 // Hence, we force a normal deleting, because the behaviour must be the same
750 // for the two kind of deleting
751 return drop_prev(false);
753 else return drop_prev(special);
757 TPushParser::drop_prev_group(bool special)
759 assert(cursor.prev() && cursor.prev().isG());
760 TNode parent = cursor.parent();
761 TNode prev = cursor.prev();
765 if (parent.isC() && prev.hasId())
767 assert(!frames.empty());
771 if (special) return "";
774 // a group could have no children, so the drop_prev is not appropriate
775 // so, this method is not equivalent to the one above
776 return do_drop(special);
781 TPushParser::drop_prev_macro(bool special)
783 assert(cursor.parent());
784 assert(cursor.prev());
785 TNode prev = cursor.prev();
788 std::string macro_name = prev.nameC();
790 TNode parent = cursor.parent();
792 const TDictionary::Entry& entry = dictionary.find(prev["name"]);
794 if (!entry.defined())
796 // We can assume that the user wants to completely delete the undefined macro,
797 // but we can also handle this case as we handle tokens. At the moment, we delete the
800 prev.replace(cursor);
801 if (cursor.parent().isC())
803 // we have removed a macro's child
804 assert(!frames.empty());
807 if (special) return "\\" + macro_name.erase(macro_name.length() - 1, 1);
812 // we start to remove a MACRO. Different actions must be taken, based on the nature
813 // of the MACRO. In some cases, we can't remove the MACRO immediately, in other
814 // cases it's correct. In the first set of cases, we have to update the stack, pushing
815 // a frame in it with a correct value of pos, in the
816 // second one, we must not push a frame in the stack
820 // In this fragment of code we also handle the leftOpen && rightOpen MACRO.
821 // if the control element is rightOpen, the cursor should be placed after
822 // the last child of the control element's last child, and than, we try to remove something.
823 // A frame MUST be pushed in the stack, because we dont' know if the following actions
824 // will completely remove the MACRO.
825 frames.push(Frame(entry));
827 // Since the MACRO is rightOpen, the last child of the MACRO must be a phantom group
828 assert(prev.last().isG() && !prev.last().hasId());
831 prev.last().append(cursor);
833 if (special) return "";
836 // the drop_prev is not appropriate, because the last child of the MACRO could have no children
837 return do_drop_phantom_group(special);
840 else if (entry.leftOpen)
842 // the leftOpen MACRO MUST have one and only one child, which MUST be a phantom group
843 // In this case, we do not have to push a frame in the stack, because we remove the
844 // MACRO immediately, substituting it with the content of the phantom group.
845 // We could remove the last child of the phantom group, but
846 // it's not clear if it's the correct behavior of the graphical deleting.
847 // At the moment, to give a standard behaviour, we remove the last element.
848 assert(prev.first());
849 assert(prev.first().isG());
850 assert(prev.first() == prev.last());
852 TNode g = prev.first();
855 // in this case, the phantom group has at least one child, so we can call the
858 prev.replace(g.first(), TNode());
859 parent.append(cursor);
860 if (special) return "\\" + macro_name.erase(macro_name.length() - 1, 1);
861 else return do_drop(special);
865 // otherwise, the phantom group has no children, so we remove it, also the MACRO.
868 prev.replace(cursor);
869 if (special) return "\\" + macro_name.erase(macro_name.length() - 1, 1);
872 // Once removed this empty macro, we could try to remove something else.
873 // This would be justified by the fact that, generally, an empty macro gives no visual information
875 return do_drop(special); // special is false
879 else if (!entry.pattern.empty())
881 // we have to start to remove a MACRO which accepts arguments.
882 // If the MACRO accepts arguments, the MACRO has at least one child
883 assert(prev.size() >= 1);
885 // Differnt actions must be taken, based on the nature of the last child
886 // of the MACRO. We have to distinguish the case in which it's a delimited argument,
887 // frome the one in which it's a not delimited argument.
888 if (prev.last().isG() && !prev.last().hasId())
892 // in this case, we have to start removing the last delimiter
893 frames.push(Frame(entry, entry.pattern.size() - 2));
896 prev.last().append(cursor);
898 std::string last_del = entry.pattern[entry.pattern.size() - 1].value;
900 return "\\" + last_del.erase(last_del.length() - 1, 1);
904 // the last argument of the MACRO is a delimited argumet. We ideally remove
905 // the sequence of delimiters
907 prev.last().append(cursor);
908 // we have to push a frame with a correct value of pos
909 assert(entry.previousParam(entry.pattern.size()) != entry.pattern.size());
911 unsigned sequence_length = entry.pattern.size() - entry.previousParam(entry.pattern.size()) - 1;
912 unsigned p = entry.pattern.size() - sequence_length - 1;
913 // now, p is the correct value of pos, and we can push the frame.
914 frames.push(Frame(entry, p));
916 // To give a standard behaviour to the graphical deleting, we remove the last
917 // element of the macro. Since we are in a phantom group, we can invoke the
918 // do_drop_phantom_group(special).
919 return do_drop_phantom_group(special);
924 // in this case, the last child of the MACRO is not a delimited argument, so we try
925 // to remove it, but we have to take differnt actions if the MACRO is a table with rows or not.
927 if (entry.table == 1 && prev.last().is("row"))
929 // in this case the cursor has to be appended to the group associated to
930 // the last cell of the last row of the table
931 assert(prev.last().last().is("cell") && prev.last().last().first().isG());
932 prev.last().last().first().append(cursor);
934 // we have to push a frame in the stack. Since tables has a pattern size = 1, we have to
935 // set pos at 0, because appending the cursor to the last cell means that this argument
936 // is not whole inserted.
937 // We don't call frames.push(Frame(entry)), because it incoditionaly set pos at 0. The
938 // following line is more general
939 frames.push(Frame(entry, entry.pattern.size() - 1));
942 // to type a table with rows and cells, the user had typed a
943 // "{", and to exit from it, the user had inserted a "}".
944 // Since we are in a special deleting, we just idealy remove the "}"
947 else return do_drop_phantom_group(special);
951 // we push a frame in the stack with a correct value of member pos.
952 // This correct value is the size of the pattern - 1, because we have been started to delete
953 // a MACRO. It means that all of the MACRO's arguments have been inserted, but
954 frames.push(Frame(entry, entry.pattern.size()));
956 return drop_prev(special);
959 } // end of the else of the if (prev.last().isG() && !prev.last().hasId())
961 } // end of if (!entry.pattern.empty())
964 // if we are here, the MACRO preceding the cursor, is !(rightOpen || leftOpen),
965 // and has no pattern. It means that it has no children.
966 // We can replace it with the cursor
967 assert(prev.size() == 0);
969 prev.replace(cursor);
970 if (cursor.parent().isC())
972 // we have removed an empty macro, which was a non delimited argument of a macro.
973 // We have to decrement pos
974 assert(!frames.empty());
978 if (special) return "\\" + macro_name.erase(macro_name.length() - 1, 1);
981 // now we could start to remove something else. This behaviour would be justified by the
982 // fact that, generally, an empty MACRO gives no visual information about it.
983 // To adopt this behaviour, just remove the comment to the following instruction
984 // return do_drop(special);
986 } // end of defined MACRO
991 TPushParser::drop_prev(bool special)
993 // if in this function, the prev of cursor does exist, also the parent and we want a graphical deleting.
995 assert(cursor.prev());
996 assert(cursor.parent());
998 TNode prev = cursor.prev();
1000 if (prev.is("i") || prev.is("o") || prev.is("n"))
1002 return drop_prev_token(special);
1004 else if (prev.isSp() || prev.isSb())
1006 return drop_prev_script(special);
1008 else if (prev.isG())
1010 return drop_prev_group(special);
1012 else if (prev.isC())
1014 // here, we also treat the case in which the MACRO is a table
1015 return drop_prev_macro(special);
1019 // not handled. Future cases...
1026 TPushParser::rgreplace_father()
1028 // this method MUST only be invoked, when the cursor
1029 // is the only child of a group with id. This function
1030 // replaces the group with the cursor. But if the new parent
1031 // is a group with id and the cursor is the only child of the
1032 // group, the new parent is replaced...and so on.
1033 // r stands for recursive, g stands for graphical.
1034 assert(cursor.parent());
1035 assert(cursor.parent().isG() && cursor.parent().hasId());
1037 TNode parent = cursor.parent();
1039 while (parent.isG() && parent.hasId() && (parent.first() == cursor))
1041 parent.replace(cursor);
1042 parent = cursor.parent();
1047 TPushParser::do_drop_script(bool special)
1049 // If we are here, the cursor is child of a script (sp or sb) and
1050 // this means that a prev does exist and that there is one and only one
1051 // element preceding the cursor. The sp's (or sb's) parent
1052 // MUST NOT be a MACRO.
1053 // The element preceding the cursor is the base of the script.
1055 assert(cursor.parent() && (cursor.parent().isSp() || cursor.parent().isSb()));
1056 TNode parent = cursor.parent();
1058 assert(parent.size() == 2);
1059 assert(parent.parent() && !parent.parent().isC());
1061 TNode prev = cursor.prev();
1063 if (prev.isG() /*&& !prev.hasId()*/ && (prev.size() == 0))
1065 // in this case, the script's base is a group with no elements, so
1066 // we have to remove the entire MACRO, replacing it with the cursor.
1067 // This situation occurs when the user had typed something like this
1074 if (special && prev.hasId())
1076 // in this case, the user has typed: ...{}^
1077 // hence we idealy remove the ^
1078 parent.replace(prev);
1079 prev.parent().append(cursor);
1082 else if (!prev.hasId())
1084 // we idealy remove the ^, but the phantom group
1085 // has to be removed, also
1087 parent.replace(cursor);
1093 parent.replace(cursor);
1095 // since the script had no children, we can try to remove something else.
1096 // Since we don't know who is cursor's parent, and who is cursor's preceding
1097 // element, we invoke the do_drop()
1098 return do_drop(special);
1101 * * the following istructions have sense, only if we remove the preceding one.
1103 // if the new parent is a group with Id and the cursor is the only
1104 // element of this group, we have to remove the group. These controls are made
1105 // in the method rgreplace_father().
1106 if (cursor.parent().isG() && cursor.parent().hasId()) rgreplace_father();
1112 // in this case, the prev has to replace the script.
1113 parent.replace(prev);
1114 prev.parent().append(cursor);
1115 // now prev have a preceding element
1116 assert(cursor.parent().size() > 1);
1118 if (special) return "";
1121 // to give a standard behaviour, we try to remove the element, which was
1122 // the script's base.
1123 return do_drop(special);
1127 } // end of method do_drop_script
1130 TPushParser::do_drop_macro(bool special)
1132 // If we are here, the cursor is a child of a MACRO and this means
1133 // that there is an open frame for the control element
1134 // and this element is closed at either side (no leftOpen no rightOpen)
1135 // and the MACRO is waiting for a not delimited argument, so
1136 // we can assert that frame.entry.pattern.size() >= 1
1137 assert(cursor.parent() && cursor.parent().isC());
1138 TNode parent = cursor.parent();
1140 // this string is useful iff we have a special deleting
1141 std::string macro_name = parent.nameC();
1143 assert(!frames.empty());
1144 Frame& frame = frames.top();
1145 assert(frame.entry.pattern.size() >= 1);
1147 // we have to take different actions, based on if a preceding element exists
1149 TNode prev = cursor.prev();
1152 // in this case, a prev does not exist, so the actions of deleting means
1153 // that we have to remove the MACRO. So we have to pop the stack.
1154 assert(frame.pos == 0);
1156 parent.replace(cursor);
1159 if (special) return "\\" + macro_name.erase(macro_name.length() - 1, 1);
1162 // Since the macro hadn't no children and this is a graphical deleting, we try
1163 // to remove something else
1164 return do_drop(special);
1167 * the following block of code has sense only if we remove the preceding instruction
1169 // if the new parent is a group with Id, and has no elements other than the
1170 // cursor, we can remove it, because it' a graphical deleting
1171 if (cursor.parent() && cursor.parent().isG() && cursor.parent().hasId())
1173 else if (cursor.parent().isC())
1175 // We have assumed that a MACRO cannot be a MACRO's child.
1176 // At the moment, this assumption is valid, but in a future
1177 // it might be false.
1178 assert(!frames.empty());
1179 Frame& frame = frames.top();
1180 assert(frame.pos > 0);
1188 // a prev does exist, we have to control if it's a delimited argument or not.
1189 if (prev.isG() && !prev.hasId())
1191 // in this case, prev is a delimited argument, so we have
1192 // to ideally remove the sequence of delimiters
1193 Frame& frame = frames.top();
1194 assert(frame.pos > 1);
1196 prev.append(cursor);
1197 assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
1201 // in this case we have to start removing the last delimimeter.
1202 // It means that we return in a situation where the user has not entirely
1203 // inserted the delimited argument. So, we have to decrement frame.pos of
1204 // two units: the delimiter and the actual argument
1205 std::string last_del = frame.entry.pattern[frame.pos - 1].value;
1206 frame.pos = frame.pos - 2;
1207 return "\\" + last_del.erase(last_del.length() - 1, 1);
1211 // these 3 lines of code update the member pos.
1212 unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
1213 assert(sequence_length);
1214 frame.pos = frame.pos - sequence_length - 1;
1216 // since it's a graphical deleting, we have to remove the current preceding element.
1217 // We don't invoke the drop_prev(), because a do_drop_phantom_group is more general.
1218 return do_drop_phantom_group(special);
1223 // the prev is not a delimited argument, so we have to try to remove it.
1224 // We "try", because the prev might be something that
1225 // a simple delete cannot remove completely
1226 return drop_prev(special);
1233 TPushParser::do_drop_groupId(bool special)
1235 // if we are here, the cursor's parent is a group with Id
1236 assert(cursor.parent() && cursor.parent().isG() && cursor.parent().hasId());
1237 TNode parent = cursor.parent();
1239 // we have to take different actions based on if the cursor has a preceding
1241 TNode prev = cursor.prev();
1244 // the cursor has a preceding element, so we try to remove it
1245 if (special) return drop_prev(special);
1248 std::string str = drop_prev(special);
1250 // We control if the group has to be removed, because the cursor
1251 // might be the only element of the group.
1252 // But we have to be careful, because drop_prev could change the TML tree
1253 // more than we think...parent could no longer exist!
1254 parent = cursor.parent();
1255 if ((parent.first() == cursor) && parent.isG() && parent.hasId())
1263 // the cursor has no preceding elements, so we have to remove the
1267 parent.replace(cursor);
1273 // we have to re-start the process, because it' a graphical deleting
1274 return do_drop(special);
1278 } // end of method do_drop_groupId()
1281 TPushParser::do_drop_phantom_group(bool special)
1283 // if we are here, the cursor MUST be a child of a
1285 assert(cursor.parent() && cursor.parent().isG() && !cursor.parent().hasId());
1287 TNode parent = cursor.parent();
1289 // now we have to control if the cursor has a preceding element or not
1290 TNode prev = cursor.prev();
1293 if (parent.parent() && parent.parent().isC())
1295 // there is a frame in the stack
1296 assert(!frames.empty());
1297 if (frames.top().entry.pattern.size())
1299 Frame& frame = frames.top();
1302 // we are in a delimited argument. If the user has inserted a proper subset of the
1303 // delimiters'sequence, we start to remove the previous delimiter. Start to remove
1304 // a delimiter means that that delimiter must be removed from the count of inserted delimiters.
1305 // It means that we have to decrement the member pos.
1306 if (frame.entry.pattern[frame.pos].category != TToken::PARAMETER)
1308 std::string del = frame.entry.pattern[frame.pos].value;
1310 return "\\" + del.erase(del.length() - 1, 1);
1315 // we are in a delimited argument. If the user has inserted a proper subset of the delimiters'sequence,
1316 // we have to remove the portion the user has inserted.
1317 while (frame.entry.pattern[frame.pos].category != TToken::PARAMETER) frame.pos--;
1322 // the cursor has a preceding element, so we try to remove it
1323 std::string str = drop_prev(special);
1325 if (special) return str;
1328 // now we have to control the parent, to handle the case of primes. But we have returned from a drop_prev(), which
1329 // could change the TML tree. So not asssuming that cursor's parent is unchanged is convenient.
1330 parent = cursor.parent();
1331 if (parent.isG() && !parent.hasId() && (parent.size() == 1) && parent.parent().isSp())
1333 // in this case the drop_prev has removed the only element preceding the cursor.
1334 // Since the phantom group is an sp's child, the user has removed all \' in the
1336 // Now we have some possibilities:
1337 // - we can replace the phantom group with the cursor, giving the user the chance to insert a new
1339 // - we can remove the phantom group and the sp element, recreating the state before the user inserted the first
1341 // At the moment we implement the second one.
1342 assert(parent.parent().size() == 2);
1343 TNode gparent = parent.parent();
1344 TNode base = gparent.first();
1347 gparent.replace(base);
1348 // now base's parent is no more gparent
1349 base.parent().append(cursor);
1353 else if (parent.isG() && !parent.hasId() && parent.parent().isSp())
1355 // in this case we have to place the cursor after the sp element
1357 assert(parent.parent().parent());
1358 parent.parent().parent().append(cursor);
1366 // in this case the cursor is the only element of the phantom group,
1367 // so we have to remove it. But, a phantom group has a special role,
1368 // so we have to control the grand father of the cursor.
1369 TNode gfather = parent.parent();
1372 // If here, the TML tree is in an inconsistent state
1373 logger.error("TML tree in a inconsistent state");
1376 else if (gfather.isC())
1378 // in this case the phantom group is child of a MACRO.
1379 // We have to control the nature of this MACRO.
1380 assert(!frames.empty());
1381 Frame& frame = frames.top();
1383 // this variable is useful in a special deleting
1384 std::string macro_name = gfather.nameC();
1386 if (frame.entry.leftOpen && frame.entry.rightOpen)
1388 // in this case, the cursor'parent is in the second and last child
1389 // of the MACRO. We can assert that the grand father has two
1390 // children, which are both phantom groups
1391 assert(gfather.size() == 2);
1392 assert((gfather.last() == parent) && (gfather.first().isG() && !gfather.first().hasId()));
1393 assert(frame.pos == 0);
1395 TNode ggfather = gfather.parent();
1399 // we have to replace the gfather with the elements of its first child, but this group may have no
1401 if (gfather.first().size())
1403 gfather.replace(gfather.first().first(), TNode());
1404 ggfather.append(cursor);
1408 // in this case, the MACRO has to be replaced with the cursor
1409 gfather.first().remove();
1410 gfather.replace(cursor);
1412 // now we have the situation preceding the insertion of the leftOpen and rightOpen MACRO.
1413 // this MACRO no longer exists.
1416 if (special) return "\\" + macro_name.erase(macro_name.length() - 1, 1);
1419 // to give a standard behaviour to the graphical deleting, we call the do_drop.
1420 return do_drop(special);
1423 else if (frame.entry.rightOpen)
1425 // the user has inserted a rightOpen MACRO, and now, this MACRO has no children (excluding the
1426 // phantom group), so we remove the MACRO.
1427 // We can assert that cursor's parent is the only child of the MACRO
1428 assert(gfather.size() == 1);
1429 assert(frame.pos == 0);
1432 gfather.replace(cursor);
1434 // now we have the situation preceding the rightOpen MACRO, so we have to pop the frame
1437 if (special) return "\\" + macro_name.erase(macro_name.length() - 1, 1);
1440 // to give a standard behaviour to the graphical deleting, we call the do_drop.
1441 return do_drop(special);
1445 else if (frame.entry.leftOpen)
1447 // this situation will never occur.
1448 logger.error("the parser has generated a wrong TML tree");
1451 else if (!frame.entry.pattern.empty())
1453 // the MACRO accepts arguments, and the phantom group in which
1454 // the cursor is, rappresents a delimited argument.
1455 // We have to control if the cursor's parent has a preceding element,
1457 TNode uncle = parent.prev();
1460 // the parent is the only element of the MACRO.
1461 // we can assert that frame.pos == 0.
1462 // In this case we can replace the MACRO with the cursor
1463 assert(frame.pos == 0);
1466 gfather.replace(cursor);
1469 if (special) return "\\" + macro_name.erase(macro_name.length() - 1, 1);
1472 // once we have replaced the empty macro with the cursor, we can remove
1474 return do_drop(special);
1479 // the parent has a preceding element. Now we have
1480 // to control if the uncle is a delimited argument or not.
1481 if (uncle.isG() && !uncle.hasId())
1483 // cursor's uncle is a delimited argument
1486 uncle.append(cursor);
1489 // we have to start removing the last delimiter of the delimited
1491 std::string last_del = frame.entry.pattern[frame.pos - 1].value;
1492 frame.pos = frame.pos - 2;
1493 return "\\" + last_del.erase(last_del.length() - 1, 1);
1497 // the uncle is a delimited argument. So we have to ideally
1498 // remove the sequence of delimiters.
1499 assert(frame.pos > 1);
1500 unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
1501 assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
1502 assert(sequence_length);
1503 // sequence_length is the length of the delimiters sequence which separates
1504 // the current parameter and the previous parameter
1505 frame.pos = frame.pos - sequence_length - 1;
1507 // once removed the sequnce of delimiters, we can start to remove the actual
1508 // parameter. We can call the do_drop_phantom_group() because a delimited argument
1509 // is always a phantom group's child
1510 return do_drop_phantom_group(special);
1515 // the uncle is a not delimited argument, so we try to remove it.
1517 parent.replace(cursor);
1518 parent = cursor.parent(); // we update the parent (it should be the MACRO)
1519 assert(parent.isC());
1521 // now we try to remove the uncle (now it' the preceding element)
1522 return drop_prev(special);
1524 } // this is the else's end, that handles the case in which an uncle exists
1525 } // end of if (!frame.entry.pattern.empty())
1528 // the entry has no arguments, is not rightOpen and is not leftOpen.
1529 logger.error("TML tree in a strange state");
1532 } // end of if (gfather.isC())
1533 else if (gfather.is("cell"))
1535 // A table is a control sequence, so there is a frame in the stack
1536 assert(!frames.empty());
1537 assert(frames.top().pos == 0);
1538 assert(frames.top().entry.table == 1);
1540 // a cell MUST be a row's child, which in turn is a table's child
1541 assert(gfather.parent() && gfather.parent().is("row") && gfather.parent().parent());
1543 // this variable is useful to handle the special deleting
1544 std::string table_name = gfather.parent().parent().nameC();
1546 TNode row = gfather.parent();
1548 // in this case the cell has no element, so the user wants to delete this cell.
1549 TNode prev_cell = gfather.prev();
1553 // now the cell no longer exists
1557 // in this case, the cell was the only cell in the row.
1558 // So, we assume that the user wants to delete the entire row.
1559 TNode table = row.parent();
1560 TNode prev_row = row.prev();
1567 // Since there was a cell (and a row), the user has typed a "{" to
1568 // We ideally remove this character.
1569 table.append(cursor);
1574 // the row was the only child of the table.
1575 // so we have to delete the entire table
1576 assert(table.parent());
1577 TNode parent_table = table.parent();
1580 parent_table.append(cursor);
1586 // there are other rows (one or more)
1587 assert(prev_row.is("row"));
1588 assert(prev_row.last());
1589 TNode last_cell = prev_row.last();
1590 assert(last_cell.is("cell"));
1591 assert(last_cell.size() == 1);
1592 assert(last_cell.first().isG() && !last_cell.first().hasId());
1593 last_cell.first().append(cursor);
1594 // Since cells and rows are separated by spaces and CRs
1595 // (and the user can see this spaces and CRs), a special deleting
1596 // is equivalent to a normal deleting
1599 } // end of if (!prev_cell)
1602 // being here means that there is a previous cell,
1603 // so we append the cursor to group.
1604 assert(prev_cell.size() == 1);
1605 assert(prev_cell.first().isG() && !prev_cell.first().hasId());
1606 prev_cell.first().append(cursor);
1609 } // end of if (gfather.is("cell"))
1610 else if (gfather.isSp())
1612 // we cannot be here because a phantom group can be a Sp child only
1613 // in two cases. If the user has typed somethong like:
1615 // the cursor is not phantom group's child.
1616 // If the user has typed somethong like
1618 // In this case the sequence of ' is placed in a phantom group,
1619 // which becomes the exponent of the script. But, the cursor is
1620 // always outside the phantom group
1621 logger.error("TML tree in a strange state");
1624 else if (gfather.is("math"))
1626 // in this case we ignore the user's will of deleting
1627 // but we could also decide to remove the math mode.
1628 logger.warning("Parser: nothing to delete");
1633 // cursor's grand father is undefined
1634 logger.error("parser: TML tree is in a unknown state");
1637 } // end of the else of the if (prev)
1643 TPushParser::do_drop(bool special)
1645 // we have to handle the case in wich the cursor has a parent or not
1646 if (!cursor.parent())
1648 // it's not a good situation...at the moment we do not take actions
1649 logger.error("TML tree not well structured");
1654 // a parent exists. We have to take differnt actions, based on the nature of
1656 TNode parent = cursor.parent();
1657 if (parent.is("math"))
1659 // we ca do two thing...we can remove the math mode (it implies controlling the display attribute), we can do nothing
1660 // At the moment, the user's will of deleting is simply ignored
1661 logger.warning("nothing to delete");
1664 else if (parent.isG())
1666 // the cursor's parent is a group. We have to control if it's a phantom group or not
1669 return do_drop_groupId(special);
1673 return do_drop_phantom_group(special);
1675 } // end of parent is group
1676 else if (parent.isC())
1678 return do_drop_macro(special);
1679 } // end of parent is a MACRO
1680 else if (parent.isSp() || parent.isSb())
1682 return do_drop_script(special);
1683 } // end of parent is sp or sb
1684 } // end of the else which consider the case in which parent exists
1686 } // end of method do_drop
1689 TPushParser::process(const TToken& token)
1691 switch (token.category)
1693 case TToken::BEGIN: return do_begin(); break;
1694 case TToken::END: return do_end(); break;
1695 case TToken::SHIFT: return do_shift(); break;
1696 case TToken::ALIGN: return do_align(); break;
1697 case TToken::EOL: return do_eol(); break;
1698 case TToken::PARAMETER: return do_parameter(token.value); break;
1699 case TToken::SUPERSCRIPT: return do_superscript(); break;
1700 case TToken::SUBSCRIPT: return do_subscript(); break;
1701 case TToken::SPACE: return do_space(token.value); break;
1702 case TToken::LETTER: return do_letter(token.value); break;
1703 case TToken::DIGIT: return do_digit(token.value); break;
1704 case TToken::OTHER: return do_other(token.value); break;
1705 case TToken::ACTIVE: return do_active(token.value); break;
1706 case TToken::COMMENT: return do_comment(); break;
1707 case TToken::CONTROL: return do_control(token.value); break;
1712 TPushParser::push(const TToken& token)
1714 TNode parent = cursor.parent();
1715 // If the cursor has no parent then it is detached from the editing
1716 // tree, which means this token will be ignored
1719 // If the parent is a phantom group and the grand-parent is a
1720 // control sequence, there are two cases:
1721 // a. we are parsing a delimited argument of a entry
1722 // b. we are parsing a side of a right- or left-open entry
1723 if (parent.isG() && !parent.hasId() && parent.parent().isC())
1725 // There must be an open frame, for the grand-parent is a control sequence
1726 assert(!frames.empty());
1727 Frame& frame = frames.top();
1728 if (!frame.entry.pattern.empty())
1730 // The entry pattern is not empty. By our conventions this means
1731 // the entry cannot be open at either end, hence we are parsing
1732 // a delimited argument
1733 assert(frame.pos + 1 < frame.entry.pattern.size());
1734 assert(frame.entry.pattern[frame.pos + 1].category != TToken::PARAMETER);
1735 if (frame.entry.pattern[frame.pos + 1] == token)
1737 // The token matches with a delimiter of the argument,
1738 // hence we increment the frame.pos
1741 if (frame.entry.lastDelimiter(frame.pos))
1743 // this delimiter is the last one for the argumet,
1744 // so the argument is completed
1751 // Delimiter mismatch.
1752 if (frame.entry.pattern[frame.pos].category != TToken::PARAMETER)
1754 // in this case, there is a sequence of delimiters that delimitates
1755 // the argument, and the user has correctly inserted a portion of this
1756 // sequence, but now has inserted a wrong delimiter.
1757 // Here, there are some possibilities:
1758 // - ignore the token, and wait for the correct delimiter
1759 // - ignore the token, wait for the correct delimiter and emit an error
1760 // At the moment, we implement the second possibily
1761 logger.warning("parser: it's not the correct delimiter...you have to type " + frame.entry.pattern[frame.pos + 1].value);
1765 // in this case, the sequence of delimiters is composed of one
1766 // delimiter. It means that we have to process the token
1773 // The entry pattern is empty, hence we are parsing a right-open
1774 // entry. What happens if we actually are in the left side?
1775 // This could happen only when re-editing an entered expression
1777 assert(frame.entry.rightOpen);
1781 else if (parent.isC())
1783 // We are parsing a non-delimited argument entry
1785 Frame& frame = frames.top();
1786 assert(frame.pos < frame.entry.pattern.size());
1788 if (frame.entry.pattern[frame.pos].category == TToken::PARAMETER)
1790 // As by the TeX parsing rules of undelimited parameters,
1791 // empty spaces are ignored
1792 if (token.category != TToken::SPACE) process(token);
1794 else if (frame.entry.pattern[frame.pos] == token)
1796 // The token has been accepted
1798 if (frame.pos < frame.entry.pattern.size() &&
1799 frame.entry.paramDelimited(frame.pos))
1801 // If the next is a delimited argument we have to place
1802 // the phantom group with the cursor inside
1803 TNode g = doc.createG();
1815 // There is a mismatch. Emit an error and ignore the token?
1816 logger.warning("parser: token ignored: " + token.value);
1823 logger.warning("ignored token");
1826 if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1830 TPushParser::drop(bool special)
1832 std::string str = do_drop(special);
1833 if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1838 TPushParser::advance(const TNode& node)
1845 logger.error("wrong TML tree");
1847 else if (node.parent().isG())
1849 TNode next = node.next();
1850 if (next) next.insert(cursor);
1851 else node.parent().append(cursor);
1853 else if (node.parent().isC())
1855 assert(!frames.empty());
1856 if ((frames.top().pos + 1 == frames.top().entry.pattern.size()) || (frames.top().entry.pattern.empty()))
1858 // we are here when we have a right open macro, or the inserted element is the last one
1859 if (frames.top().entry.rightOpen)
1861 // we have to remove the frame from the stack
1863 advance(node.parent().parent());
1868 advance(node.parent());
1871 else if (frames.top().entry.paramDelimited(frames.top().pos + 1))
1873 // the next argument is delimited, so we have to create a phantom group
1874 TNode g = doc.createG();
1876 node.parent().append(g);
1881 // the next argumet is not delimited, so we have to append the cursor
1883 node.parent().append(cursor);
1887 else advance(node.parent());
1891 TPushParser::setCursorHint(const std::string& c)
1893 if (cursor["val"] != c)
1896 if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1901 TPushParser::hideCursor()
1903 if (hiddenCursor++ == 0)
1905 cursor["visible"] = "0";
1906 if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1914 TPushParser::showCursor()
1916 if (hiddenCursor > 0 && --hiddenCursor == 0)
1918 cursor["visible"] = "1";
1919 if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1929 if (APushParser::thaw() && factory && doc.dirtyNode())
1931 factory->documentModified(doc);