]> matita.cs.unibo.it Git - helm.git/blob - helm/DEVEL/mathml_editor/src/TPushParser.cc
ocaml 3.09 transition
[helm.git] / helm / DEVEL / mathml_editor / src / TPushParser.cc
1 /* This file is part of EdiTeX, an editor of mathematical
2  * expressions based on TeX syntax.
3  * 
4  * Copyright (C) 2002-2003 Luca Padovani <lpadovan@cs.unibo.it>,
5  *                    2003 Paolo Marinelli <pmarinel@cs.unibo.it>.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * For more information, please visit the project's home page
22  * http://helm.cs.unibo.it/editex/
23  * or send an email to <lpadovan@cs.unibo.it>
24  */
25
26 #include <cassert>
27
28 #include "ALogger.hh"
29 #include "TPushParser.hh"
30 #include "AMathMLFactory.hh"
31
32 TPushParser::TPushParser(ALogger& l, const TDictionary& d) : APushParser(l), dictionary(d)
33 {
34   init();
35 }
36
37 TPushParser::TPushParser(ALogger& l, AMathMLFactory& f, const TDictionary& d) : APushParser(l, f), dictionary(d)
38 {
39   init();
40 }
41
42 TPushParser::~TPushParser()
43 {
44 }
45
46 void
47 TPushParser::init()
48 {
49   cursor = doc.create("cursor");
50   cursor["visible"] = "1";
51   hiddenCursor = 0;
52   reset();
53 }
54
55 void
56 TPushParser::reset()
57 {
58   nextId = 1;
59   if (cursor.parent()) cursor.remove();
60   cursor["val"] = "";
61   doc.reset();
62   doc.root().append(cursor);
63   if (factory && !frozen()) factory->documentModified(doc);
64 }
65
66 TNode
67 TPushParser::PRIME()
68 {
69   const TDictionary::Entry entry = dictionary.find("prime");
70   if (entry.cls == TDictionary::OPERATOR)
71     {
72       TNode op = doc.createO(entry.value, nextId++);
73       op["name"] = "prime";
74       return op;
75     }
76   else
77     {
78       TNode op = doc.createO("?", nextId++);
79       return op;
80     }
81 }
82
83 bool
84 TPushParser::do_begin()
85 {
86   TNode parent = cursor.parent();
87   if (parent.isC() && dictionary.find(parent.nameC()).table)
88     {
89       TNode row = doc.create("row");
90       TNode cell = doc.create("cell");
91       TNode g = doc.createG();
92       row.append(cell);
93       cell.append(g);
94       g.append(cursor);
95       parent.append(row);
96     }
97   else
98     {
99       TNode g = doc.createG(nextId++);
100       cursor.replace(g);
101       g.append(cursor);
102     }
103   return true;
104 }
105
106 bool
107 TPushParser::correctBrace()
108 {
109   // this method MUST be invoked when the cursor is child of a 
110   // phantom group, which in turn is the last rightOpen MACRO's child.
111   // The only way to exit from a rightOpen MACRO is opening a group before 
112   // inserting the MACRO and, once the MACRO is completely inserted, closing 
113   // the group.
114   // This method return true if the condition above is true. False, otherwise.
115   assert(cursor.parent() && cursor.parent().isG() && !cursor.parent().hasId());
116   TNode parent = cursor.parent();
117   assert(parent.parent() && parent.parent().isC());
118   assert(!frames.empty());
119   Frame& frame = frames.top();
120   assert(frame.entry.rightOpen);
121   assert(parent.parent().last() == parent);
122   
123   TNode c = parent.parent();
124   bool stop = false;
125   bool ok = false;
126   TNode node = c.parent();
127   do
128     {
129       if (node.isG() && node.hasId())
130         {
131           // in this case, the rightOpen MACRO is a child of a group with id. 
132           // So, the '}' is correct
133           ok = true;
134           stop = true;
135         }
136       else if (node.isG())
137         {
138           // the MACRO is a phantom group's child. We have to control why we 
139           // have this phantom group
140           TNode nodeParent = node.parent();
141           if (nodeParent && nodeParent.isC())
142             {
143               // we have to control the nature of this MACRO
144               const TDictionary::Entry& entry = dictionary.find(nodeParent.nameC());
145               if (entry.rightOpen && node == nodeParent.last())
146                 {
147                   // in this case we have to re-iterate the process
148                   node = nodeParent.parent();
149                 }
150               else stop = true;
151             }
152           else stop = true;
153         }
154       else
155         {
156           // at the moment we assume that a MACRO cannot be child of an element other than a group
157           stop = true;
158         }
159     }
160   while (!stop);
161
162   return ok;
163 }
164
165 bool
166 TPushParser::do_end()
167 {
168   TNode parent = cursor.parent();
169   if (parent && parent.isG() && parent.hasId())
170     {
171       // normal closing brace for an explicitly open group
172       cursor.remove();
173       advance(parent);
174       return true;
175     }
176   else if (parent && parent.isG() && parent.parent() && parent.parent().is("cell"))
177     {
178       assert(!frames.empty());
179       // closing brace for a structure in which & or \cr have been used
180       TNode row = parent.parent().parent();
181       assert(row && row.is("row"));
182       assert(row.parent());
183       advance(row);
184       return true;
185     }
186   else if (parent && parent.isG() && !parent.hasId() && parent.parent() && !parent.parent().is("math"))
187     {
188       // In this case, we have to control the cursor's grand parent.
189       TNode gparent = parent.parent();
190
191       if (gparent.isC() && gparent.last() == parent)
192         {
193           // a frame MUST be in the stack
194           assert(!frames.empty());
195
196           // we have to control the nature of this macro
197           if (frames.top().entry.rightOpen)
198             {
199               // in this case, the '}' character is the proper way to exit from the phantom group, and 
200               // in particular, this character means that the user wants to exit from the MACRO.
201               // A rightOpen MACRO MUST be descendant of a group with Id. This '}' is the closing brace of this 
202               // group. So, we have to control if this group exists. This groyp could exist, but this MACRO could 
203               // be another MACRO's child, so we have to control this last MACRO recursively. This recurive control 
204               // is done by the correctBrace method.
205               if (!correctBrace())
206                 {
207                   // the '}' is not correct
208                   logger.warning("nothing to close");
209                   return false;
210                 }
211               else
212                 {
213                   cursor.remove();
214                   advance(parent);
215                   return true;
216                 }
217             }
218           else
219             {
220               logger.warning("ignored closing brace");
221               return false;
222             }
223         }
224       else
225         {
226           // at the moment, a phantom group with the cursor inside can be a MACRO's child or a cell's child, and these cases
227           // are handled in other blocks of code.
228           logger.error("do_end: strange TML tree");
229           return false;
230         }
231     }
232   else
233     {
234       // In this case, there is a redundant '}', so we can ignore it and 
235       // emit an error
236       logger.warning("There is so no corresponding'{'");
237       return false;
238       //assert(0);
239     }
240 }
241
242 bool
243 TPushParser::do_shift()
244 {
245   TNode parent = cursor.parent();
246   assert(parent);
247   if (parent.is("tex"))
248     {
249       TNode math = doc.create("math", nextId++);
250       TNode g = doc.createG();
251       cursor.replace(math);
252       math.append(g);
253       g.append(cursor);
254       return true;
255     }
256   else if (parent.isG() && !parent.hasId() && parent.parent() && parent.parent().is("math"))
257     {
258       if (cursor.prev())
259         {
260           // there is something before the cursor, hence this is the
261           // closing math shift
262           if (parent.parent()["display"] != "1")
263             {
264               // one math shift is enough to close it
265               cursor.remove();
266               return true;
267             }
268           else
269             {
270               // we need two closing math shifts
271               //cursor.remove(); ??
272               parent.parent().append(cursor);
273               return true; // ???
274             }
275         }
276       else if (parent.parent()["display"] != "1")
277         {
278           // there is nothing before the cursor, and the math is not
279           // in display mode, so this must be a double math shift
280           parent.parent()["display"] = "1";
281           return true;
282         }
283       else
284         {
285           parent.parent().append(cursor);
286           return true;
287         }
288     }
289   else if (parent.is("math"))
290     {
291       cursor.remove();
292       return true;
293     }
294   else
295     {
296       logger.warning("not allowed here");
297       return false;
298     }
299 }
300
301 bool
302 TPushParser::do_align()
303 {
304   TNode parent = cursor.parent();
305   if (parent && parent.isG() && parent.hasId())
306     {
307       // alignment tab used for the first time inside a group
308       TNode row = doc.create("row");
309       TNode cell = doc.create("cell");
310       TNode g = doc.createG();
311       row.append(cell);
312       cell.append(g);
313       g.append(parent.first(), cursor);
314       return true;
315     }
316   else if (parent && parent.isG() && parent.parent().is("cell"))
317     {
318       // alignment tab used within a cell
319       TNode oldCell = parent.parent();
320       assert(oldCell && oldCell.is("cell"));
321       TNode row = oldCell.parent();
322       assert(row && row.is("row"));
323       TNode cell = doc.create("cell");
324       if (oldCell.next()) oldCell.next().insert(cell);
325       else row.append(cell);
326       TNode g = doc.createG();
327       cell.append(g);
328       g.append(cursor);
329       return true;
330     }
331   else
332     {
333       logger.warning("alignment tab used outside matrix");
334       return false;
335     }
336 }
337
338 bool
339 TPushParser::do_eol()
340 {
341   //if (cursor.parent()) cursor.remove();
342   logger.warning("ignored token");
343   return false;
344 }
345
346 bool
347 TPushParser::do_parameter(const std::string& p)
348 {
349   logger.warning("ignored token");
350   return false;
351 }
352
353 bool
354 TPushParser::do_subscript()
355 {
356   TNode parent = cursor.parent();
357   if (parent.isG())
358     {
359       TNode prev = cursor.prev();
360       if (!prev)
361         {
362           TNode elem = doc.create("sb", nextId++);
363           TNode g = doc.createG();
364           cursor.replace(elem);
365           elem.append(g);
366           elem.append(cursor);
367           return true;
368         }
369       else
370         {
371           TNode elem = doc.create("sb", nextId++);
372           prev.replace(elem);
373           elem.append(prev);
374           elem.append(cursor);
375           return true;
376         }
377     }
378   else if (parent.isSb() && cursor == parent[1])
379     {
380       if (parent["under"] == "1")
381         {
382           logger.warning("already under");
383           return false;
384         }
385       else
386         {
387           parent["under"] = "1";
388           return true;
389         }
390     }
391   else
392     {
393       logger.warning("ignored token");
394       return false;
395     }
396 }
397
398 bool
399 TPushParser::do_superscript()
400 {
401   TNode parent = cursor.parent();
402   if (parent.isG())
403     {
404       TNode prev = cursor.prev();
405       if (!prev)
406         {
407           TNode elem = doc.create("sp", nextId++);
408           TNode g = doc.createG();
409           cursor.replace(elem);
410           elem.append(g);
411           elem.append(cursor);
412           return true;
413         }
414       else
415         {
416           TNode elem = doc.create("sp", nextId++);
417           prev.replace(elem);
418           elem.append(prev);
419           elem.append(cursor);
420           return true;
421         }
422     }
423   else if (parent.isSp() && cursor == parent[1])
424     {
425       if (parent["over"] == "1")
426         {
427           logger.warning("already over");
428           return false;
429         }
430       else
431         {
432           parent["over"] = "1";
433           return true;
434         }
435     }
436   else
437     {
438       logger.warning("ignored token");
439       return false;
440     }
441 }
442
443 bool
444 TPushParser::do_ignorablespace(const std::string& s)
445 {
446   // At the moment, do nothing
447 }
448
449 bool
450 TPushParser::do_space(const std::string&)
451 {
452   TNode elem = doc.createS(nextId++);
453   cursor.replace(elem);
454   advance(elem);
455   return true;
456 }
457
458 bool
459 TPushParser::do_letter(const std::string& s)
460 {
461   //TNode parent = cursor.parent();
462   TNode elem = doc.createI(s, nextId++);
463   cursor.replace(elem);
464   advance(elem);
465   return true;
466 }
467
468 bool
469 TPushParser::do_digit(const std::string& s)
470 {
471   TNode elem = doc.createN(s, nextId++);
472   cursor.replace(elem);
473   advance(elem);
474   return true;
475 }
476
477 bool
478 TPushParser::isPrimes(const TNode& node) const
479 {
480   assert(node);
481   return node.isG() && node.last() && node.last().is("o") && node.last()["name"] == "prime";
482 }
483
484 bool
485 TPushParser::do_apostrophe()
486 {
487   if (cursor.parent() && cursor.parent().isG())
488     {
489       if (TNode prev = cursor.prev())
490         {
491           if (prev.isSp() && prev[1] && isPrimes(prev[1]))
492             {
493               prev[1].append(PRIME());
494               return true;
495             }
496           else if (prev.isSb() && prev[0] &&
497                    prev[0].isSp() && prev[0][1] &&
498                    isPrimes(prev[0][1]))
499             {
500               prev[0][1].append(PRIME());
501               return true;
502             }
503           else
504             {
505               TNode elem = doc.create("sp");
506               TNode g = doc.createG();
507               prev.replace(elem);
508               elem.append(prev);
509               elem.append(g);
510               g.append(PRIME());
511               return true;
512             }
513         }
514       else
515         {
516           // is it an error?
517           logger.warning("you have to insert an identifier before a  ''");
518           return false;
519         }
520     }
521   else
522     {
523       logger.warning("cursor has to be in a group");
524       return false;
525     }
526 }
527
528 bool
529 TPushParser::do_other(const std::string& s)
530 {
531   switch (s[0])
532     {
533     case '\'':
534       return do_apostrophe();
535       break;
536     default:
537       /*cout << "TPushParser::do_other " << s << endl;
538       cout << "DOCUMENT: " << static_cast<GdomeNode*>(cursor.element().get_ownerDocument()) << endl;*/
539       TNode elem = doc.createT("o", s, nextId++);
540       cursor.replace(elem);
541       advance(elem);
542       return true;
543       break;
544     }
545 }
546
547 bool
548 TPushParser::do_active(const std::string&)
549 {
550   // ??? space?
551   logger.warning("ignored token");
552   return false;
553 }
554
555 bool
556 TPushParser::do_comment()
557 {
558   // ???
559   return false;
560 }
561
562 bool
563 TPushParser::do_cr()
564 {
565   TNode parent = cursor.parent();
566   if (parent && parent.isG() &&
567       parent.parent() && parent.parent().is("cell") &&
568       parent.parent().parent() && parent.parent().parent().is("row"))
569     {
570       TNode oldRow = parent.parent().parent();
571       assert(oldRow);
572       TNode table = oldRow.parent();
573       assert(table);
574       TNode row = doc.create("row");
575       TNode cell = doc.create("cell");
576       TNode g = doc.createG();
577       if (oldRow.next()) oldRow.next().insert(row);
578       else table.append(row);
579       row.append(cell);
580       cell.append(g);
581       g.append(cursor);
582       return true;
583     }
584   else
585     {
586       // at the moment, \cr can only be used inside a table
587       logger.warning("cr used outside a table");
588       return false;
589     }
590 }
591
592 bool
593 TPushParser::do_control(const std::string& name)
594 {
595   if (name == "cr") return do_cr();
596   else
597     {
598       TNode parent = cursor.parent();
599       const TDictionary::Entry& entry = dictionary.find(name);
600       switch (entry.cls)
601         {
602         case TDictionary::IDENTIFIER:
603           {
604             TNode t = doc.createI(entry.value, nextId++);
605             t["name"] = name;
606             cursor.replace(t);
607             advance(t);
608             return true;
609           }
610           break;
611         case TDictionary::OPERATOR:
612           {
613             TNode t = doc.createO(entry.value, nextId++);
614             t["name"] = name;
615             cursor.replace(t);
616             advance(t);
617             return true;
618           }
619           break;
620         case TDictionary::NUMBER:
621           {
622             TNode t = doc.createN(entry.value, nextId++);
623             t["name"] = name;
624             cursor.replace(t);
625             advance(t);
626             return true;
627           }
628           break;
629         case TDictionary::MACRO:
630           {
631             if (parent.isG())
632               {
633                 TNode m = doc.createC(name, nextId++);
634                 cursor.replace(m);
635                 if (entry.leftOpen && entry.rightOpen)
636                   {
637                     assert(entry.pattern.empty());
638                     assert(parent.isG());
639                     TNode g1 = doc.createG();
640                     g1["left-open"] = "1";
641                     g1.append(parent.first(), m);
642                     m.append(g1);
643                     TNode g2 = doc.createG();
644                     g2.append(cursor);
645                     m.append(g2);
646                     frames.push(Frame(entry));
647                   }
648                 else if (entry.leftOpen)
649                   {
650                     assert(parent.isG());
651                     TNode g = doc.createG();
652                     g["left-open"] = "1";
653                     g.append(parent.first(), m);
654                     m.append(g);
655                     advance(m);
656                   }
657                 else if (entry.rightOpen)
658                   {
659                     assert(entry.pattern.empty());
660                     assert(parent.isG());
661                     TNode g = doc.createG();
662                     g.append(cursor);
663                     m.append(g);
664                     frames.push(Frame(entry));
665                   }
666                 else if (!entry.pattern.empty())
667                   {
668                     frames.push(Frame(entry));
669                     if (entry.paramDelimited(0))
670                       {
671                         TNode g = doc.createG();
672                         m.append(g);
673                         g.append(cursor);
674                       }
675                     else
676                       m.append(cursor);
677                   }
678                 else
679                   {
680                     // it's an empty macro
681                     advance(m);
682                   }
683                 return true;
684               }
685             else if (!entry.pattern.size() && !entry.rightOpen && !entry.leftOpen)
686               {
687                 // a macro with no arguments and no right open and no left open, can be child of anything
688                 TNode m = doc.createC(name, nextId++);
689                 cursor.replace(m);
690                 advance(m);
691                 return true;
692               }
693             else
694               {
695                 // a macro with arguments or a rightOpen or leftOpen macro must be a group's child
696                 logger.warning("ignored token: this macro should be in a group");
697                 return false;
698               }
699           }
700           break;
701         case TDictionary::UNDEFINED:
702           {
703             logger.warning("using undefined macro " + name);
704             TNode m = doc.createC(name, nextId++);
705             cursor.replace(m);
706             advance(m);
707             return true;
708           }
709           break;
710         default:
711           {
712             //assert(0);
713             logger.warning("ignored token");
714             return false;
715           }
716         }
717     }
718 }
719
720 std::string
721 TPushParser::drop_prev_token(bool special)
722 {
723   assert(cursor.prev());
724   assert(cursor.parent());
725   TNode prev = cursor.prev();
726   assert(prev.isT());
727  
728   DOM::UCS4String ucs4val = prev.element().getAttribute("val");
729   bool macro = prev.element().hasAttribute("name");
730   std::string utf8name;
731   if (macro) utf8name = prev.element().getAttribute("name");
732   
733   cursor.remove();
734   prev.replace(cursor);
735   
736   if (cursor.parent().isC())
737     {
738       // in this case we have removed an element of a MACRO. 
739       // we can assert that this element was a non delimited argument
740       assert(!frames.empty());
741       Frame& frame = frames.top();
742       assert(frame.pos > 0);
743       frame.pos--;
744     }
745
746   if ((ucs4val.length() > 1))
747     {
748       if (!macro)
749         {
750           // in this case we can return the content of ucs4val, but we have 
751           // to convert it in a utf8
752           DOM::GdomeString gdsval(ucs4val);
753           std::string utf8val(gdsval);
754           switch (utf8val[utf8val.length() - 1])
755             {
756             case '-':
757             case '_':
758               return (special) ? std::string(utf8val, 0, utf8val.length() - 1) + "\\" : std::string(utf8val, 0, utf8val.length() - 1);
759             default: return (special) ? "" : std::string(utf8val, 0, utf8val.length() - 1);
760             }
761         }
762       else
763         {
764           // in this case, the content of val could be in unicode, 
765           // but we have the attribute name, which doesn't contain character not representable 
766           // with a byte.
767           return (special) ? "\\" + utf8name : "";
768         }
769     }
770   else if (macro && special) return "\\" + utf8name;
771   else return "";
772 }
773
774 std::string
775 TPushParser::drop_prev_script(bool special)
776 {
777   // this method deletes an sp or an sb preceding the cursor
778   assert(cursor.prev());
779   assert(cursor.parent());
780   TNode prev = cursor.prev();
781   assert(prev.is("sp") || prev.is("sb"));
782   cursor.remove();
783   prev.append(cursor);
784   // we can invoke the drop_prev, because a sp (sb) MUST have two children
785   // but we cannot invoke do_drop_script because it assumes when called, the first 
786   // child has been removed yet.
787   if (cursor.prev().isG() && !prev.hasId())
788     {
789       // in this case, the user has inserted a sequence of '.
790       // Hence, we force a normal deletion, because the behavior must be the same 
791       // for the two kind of deletion
792       return drop_prev(false);
793     }
794   else return drop_prev(special);
795 }
796
797 std::string
798 TPushParser::drop_prev_group(bool special)
799 {
800   assert(cursor.prev() && cursor.prev().isG());
801   TNode parent = cursor.parent();
802   TNode prev = cursor.prev();
803   cursor.remove();
804   prev.append(cursor);
805
806   if (parent.isC() && prev.hasId())
807     {
808       // this previous group is a macro's argument. Entering inside it means that
809       // this argument becomes incomplete. Hence, we have to decrement the member pos.
810       assert(!frames.empty());
811       frames.top().pos--;
812     }
813
814   if (special) return "";
815   else
816     {
817       // a group could have no children, so the drop_prev is not appropriate
818       // so, this method is not equivalent to the one above
819       return do_drop(special);
820     }
821 }
822
823 std::string
824 TPushParser::drop_prev_macro(bool special)
825 {
826   assert(cursor.parent());
827   assert(cursor.prev());
828   TNode prev = cursor.prev();
829   assert(prev.isC());
830
831   std::string macro_name = prev.nameC();
832
833   TNode parent = cursor.parent();
834   
835   const TDictionary::Entry& entry = dictionary.find(prev["name"]);
836   
837   if (!entry.defined())
838     {
839       // In this case, with a normal deletion, we completely remove the macro.
840       // With a special deletion, we remove the last character of the macro's name.
841       cursor.remove();
842       prev.replace(cursor);
843       if (cursor.parent().isC())
844         {
845           // we have removed a macro's child
846           assert(!frames.empty());
847           frames.top().pos--;
848         }
849       if (special) return "\\" + macro_name.erase(macro_name.length() - 1, 1); // we remove the last char, because an undefined macro's name is visible
850       return "";
851     }
852   else
853     {   
854       // we start to remove a MACRO. Different actions must be taken, based on the nature 
855       // of the MACRO. In some cases, we can't remove the MACRO immediately, in other
856       // cases it's correct. In the first set of cases, we have to update the stack, pushing
857       // a frame in it with a correct value of pos, in the 
858       // second one, we must not push a frame in the stack
859       
860       if (entry.rightOpen)
861         {
862           // In this fragment of code we also handle the leftOpen && rightOpen MACRO.
863           // since the control element is rightOpen, the cursor should be placed after 
864           // the last child of the control element's last child, and than, we try to remove something.
865           // A frame MUST be pushed in the stack, because we dont' know if the following actions 
866           // will completely remove the MACRO.
867           frames.push(Frame(entry));
868
869           // Since the MACRO is rightOpen, the last child of the MACRO must be a phantom group
870           assert(prev.last().isG() && !prev.last().hasId());
871           
872           cursor.remove();
873           prev.last().append(cursor);
874
875           if (special) return "";
876           else
877             {
878               // the drop_prev is not appropriate, because the last child of the MACRO could have no children
879               return do_drop_phantom_group(special);
880             }
881         }
882       else if (entry.leftOpen)
883         {
884           // the leftOpen MACRO MUST have one and only one child, which MUST be a phantom group
885           // In this case, we do not have to push a frame in the stack, because we remove the 
886           // MACRO immediately, substituting it with the content of the phantom group.
887           // We could remove the last child of the phantom group, but
888           // it's not clear if it's the correct behavior of the graphical deletion.
889           // At the moment, to give a standard behavior, we remove the last element.
890           // With a special deletion, we do not remove it.
891           assert(prev.first());
892           assert(prev.first().isG());
893           assert(prev.first() == prev.last());
894           
895           TNode g = prev.first();
896           if (g.size())
897             {
898               // in this case, the phantom group has at least one child, so we can call the 
899               // TNode::replace.
900               g.remove();
901               prev.replace(g.first(), TNode());
902               parent.append(cursor);
903               if (special) return "\\" + macro_name;
904               else return do_drop(special);
905             }
906           else
907             {
908               // otherwise, the phantom group has no children, so we remove it, also the MACRO.
909               cursor.remove();
910               g.remove();
911               prev.replace(cursor);
912               if (special) return "\\" + macro_name;
913               else
914                 {
915                   // Once removed this empty macro, we could try to remove something else.
916                   // This would be justified by the fact that, generally, an empty macro gives no visual information 
917                   // about it.
918                   return do_drop(special); // special is false
919                 }
920             }
921         }
922       else if (!entry.pattern.empty())
923         {
924           // we have to start to remove a MACRO which accepts arguments.
925           // If the MACRO accepts arguments, the MACRO has at least one child     
926           assert(prev.size() >= 1);
927
928           // Differnt actions must be taken, based on the nature of the last child
929           // of the MACRO. We have to distinguish the case in which it's a delimited argument,
930           // frome the one in which it's a not delimited argument.
931           if (prev.last().isG() && !prev.last().hasId())
932             {
933               if (special)
934                 {
935                   // in this case, we have to start removing the last delimiter
936                   frames.push(Frame(entry, entry.pattern.size() - 2));
937                   
938                   cursor.remove();
939                   prev.last().append(cursor);
940
941                   std::string last_del = entry.pattern[entry.pattern.size() - 1].value;
942
943                   return "\\" + last_del;
944                 }
945               else
946                 {
947                   // the last argument of the MACRO is a delimited argumet. We ideally remove 
948                   // the sequence of delimiters
949                   cursor.remove();
950                   prev.last().append(cursor);
951                   //  we have to push a frame with a correct value of pos
952                   assert(entry.previousParam(entry.pattern.size()) != entry.pattern.size());
953                   
954                   unsigned sequence_length = entry.pattern.size() - entry.previousParam(entry.pattern.size()) - 1;
955                   unsigned p = entry.pattern.size() - sequence_length - 1;
956                   // now, p is the correct value of pos, and we can push the frame.
957                   frames.push(Frame(entry, p));
958                   
959                   // To give a standard behavior to the graphical deletion, we remove the last 
960                   // element of the macro. Since we are in a phantom group, we can invoke the 
961                   // do_drop_phantom_group(special).
962                   return do_drop_phantom_group(special);
963                 }
964             }
965           else
966             {
967               // in this case, the last child of the MACRO is not a delimited argument, so we try 
968               // to remove it, but we have to take differnt actions if the MACRO is a table with rows or not. 
969               cursor.remove();
970               if (entry.table == 1 && prev.last().is("row"))
971                 {
972                   // in this case the cursor has to be appended to the group associated to 
973                   // the last cell of the last row of the table
974                   assert(prev.last().last().is("cell") && prev.last().last().first().isG());
975                   prev.last().last().first().append(cursor);
976                   
977                   // we have to push a frame in the stack. Since tables has a pattern size = 1, we have to 
978                   // set pos at 0, because appending the cursor to the last cell means that this argument 
979                   // is not whole inserted.
980                   // We don't call frames.push(Frame(entry)), because it incoditionaly set pos at 0. The 
981                   // following line is more general
982                   frames.push(Frame(entry, entry.pattern.size() - 1));
983                   if (special)
984                     {
985                       // to type a table with rows and cells, the user had typed a 
986                       // "{", and to exit from it, the user had inserted a "}".
987                       // Since we are in a special deletion, we just idealy remove the "}"
988                       return "";
989                     }
990                   else return do_drop_phantom_group(special);
991                 }
992               else
993                 {
994                   // we push a frame in the stack with a correct value of member pos.
995                   // This correct value is the size of the pattern - 1, because we have been started to delete 
996                   // a MACRO. It means that all of the MACRO's arguments have been inserted, but 
997                   frames.push(Frame(entry, entry.pattern.size()));
998                   prev.append(cursor);
999                   return drop_prev(special);
1000                 }
1001
1002             } // end of the else of the if (prev.last().isG() && !prev.last().hasId())
1003
1004         } // end of if (!entry.pattern.empty())
1005       else
1006         {
1007           // if we are here, the MACRO preceding the cursor, is !(rightOpen || leftOpen),
1008           // and has no pattern. It means that it has no children.
1009           // We can replace it with the cursor
1010           assert(prev.size() == 0);
1011           cursor.remove();
1012           prev.replace(cursor);
1013           if (cursor.parent().isC())
1014             {
1015               // we have removed an empty macro, which was a non delimited argument of a macro.
1016               // We have to decrement pos
1017               assert(!frames.empty());
1018               frames.top().pos--;
1019             }
1020
1021           if (special) return "\\" + macro_name;
1022           else return "";
1023                   
1024           // now we could start to remove something else. This behavior would be justified by the 
1025           // fact that, generally, an empty MACRO gives no visual information about it.
1026           // To adopt this behavior, just remove the comment to the following instruction
1027           // return do_drop(special);
1028         }
1029     } // end of defined MACRO
1030
1031 }
1032
1033 std::string
1034 TPushParser::drop_prev(bool special)
1035 {
1036   // if in this function, the prev of cursor does exist, also the parent and we want a graphical deletion.
1037   
1038   assert(cursor.prev());
1039   assert(cursor.parent());
1040
1041   TNode prev = cursor.prev();
1042
1043   if (prev.isT())
1044     {
1045       return drop_prev_token(special);
1046     }
1047   else if (prev.isSp() || prev.isSb())
1048     {
1049       return drop_prev_script(special);
1050     }
1051   else if (prev.isG())
1052     {
1053       return drop_prev_group(special);
1054     }
1055   else if (prev.isC())
1056     {
1057       // here, we also treat the case in which the MACRO is a table
1058       return drop_prev_macro(special);
1059     }
1060   else 
1061     {
1062       // not handled. Future cases...
1063       return "";
1064     }
1065
1066 } // end of method
1067
1068 void
1069 TPushParser::rgreplace_father()
1070 {
1071   // this method MUST only be invoked, when the cursor
1072   // is the only child of a group with id. This function 
1073   // replaces the group with the cursor. But if the new parent
1074   // is a group with id and the cursor is the only child of the 
1075   // group, the new parent is replaced...and so on.
1076   // r stands for recursive, g stands for graphical.
1077   assert(cursor.parent());
1078   assert(cursor.parent().isG() && cursor.parent().hasId());
1079
1080   TNode parent = cursor.parent();
1081
1082   while (parent.isG() && parent.hasId() && (parent.first() == cursor))
1083     {
1084       parent.replace(cursor);
1085       parent = cursor.parent();
1086     }
1087 }
1088
1089 std::string
1090 TPushParser::do_drop_script(bool special)
1091 {
1092   // If we are here, the cursor is child of a script (sp or sb) and 
1093   // this means that a prev does exist and that there is one and only one 
1094   // element preceding the cursor. The sp's (or sb's) parent 
1095   // MUST NOT be a MACRO.
1096   // The element preceding the cursor is the base of the script.
1097
1098   assert(cursor.parent() && (cursor.parent().isSp() || cursor.parent().isSb()));
1099   TNode parent = cursor.parent();
1100           
1101   assert(parent.size() == 2);
1102   assert(parent.parent() && !parent.parent().isC());
1103   
1104   TNode prev = cursor.prev();
1105   cursor.remove();
1106   if (prev.isG() /*&& !prev.hasId()*/ && (prev.size() == 0))
1107     {
1108       // in this case, the script's base is a group with no elements, so 
1109       // we have to remove the entire MACRO, replacing it with the cursor.
1110       // This situation occurs when the user had typed something like this
1111       //   $....{}^
1112       // or this 
1113       //   $^
1114       // or this
1115       //   $...{^
1116       //
1117       if (special && prev.hasId())
1118         {
1119           // in this case, the user has typed: ...{}^
1120           // hence we idealy remove the ^
1121           parent.replace(prev);
1122           prev.parent().append(cursor);
1123           return "";
1124         }
1125       else if (!prev.hasId())
1126         {
1127           // we idealy remove the ^, but the phantom group 
1128           // has to be removed, also
1129           prev.remove();
1130           parent.replace(cursor);
1131           return "";
1132         }
1133       else
1134         {
1135           prev.remove();
1136           parent.replace(cursor);
1137           
1138           // since the script had no children, we can try to remove something else.
1139           // Since we don't know who is cursor's parent, and who is cursor's preceding 
1140           // element, we invoke the do_drop()
1141           return do_drop(special);
1142         }
1143     }
1144   else
1145     {
1146       // in this case, the prev has to replace the script.
1147       parent.replace(prev);
1148       prev.parent().append(cursor);
1149       // now prev have a preceding element
1150       assert(cursor.parent().size() > 1);
1151
1152       if (special) return "";
1153       else
1154         {
1155           // to give a standard behavior, we try to remove the element, which was 
1156           // the script's base.
1157           return do_drop(special);
1158         }
1159     }
1160   
1161 } // end of method do_drop_script
1162
1163 std::string
1164 TPushParser::do_drop_macro(bool special)
1165 {
1166   // If we are here, the cursor is a child of a MACRO and this means
1167   // that there is an open frame for the control element
1168   // and this element is closed at either side (no leftOpen no rightOpen)
1169   // and the MACRO is waiting for a not delimited argument, so 
1170   // we can assert that frame.entry.pattern.size() >= 1
1171   assert(cursor.parent() && cursor.parent().isC());
1172   TNode parent = cursor.parent();
1173
1174   // this string is useful iff we have a special deletion.
1175   std::string macro_name = parent.nameC();
1176   
1177   assert(!frames.empty());
1178   Frame& frame = frames.top();
1179   assert(frame.entry.pattern.size() >= 1);
1180
1181   // we have to take different actions, based on if a preceding element exists 
1182   // or not
1183   TNode prev = cursor.prev();
1184   if (!prev)
1185     {
1186       // in this case, a prev does not exist, so the actions of deleting means 
1187       // that we have to remove the MACRO. So we have to pop the stack.
1188       assert(frame.pos == 0);
1189       
1190       parent.replace(cursor);
1191       frames.pop();
1192
1193       if (special) return "\\" + macro_name;
1194       else
1195         {
1196           // Since the macro had no children and this is a graphical deletion, we try 
1197           // to remove something else
1198           return do_drop(special);
1199         }
1200     }
1201   else
1202     {
1203       // a prev does exist, we have to control if it's a delimited argument or not.
1204       if (prev.isG() && !prev.hasId())
1205         {
1206           // in this case, prev is a delimited argument, so we have 
1207           // to ideally remove the sequence of delimiters
1208           Frame& frame = frames.top();
1209           assert(frame.pos > 1);
1210           cursor.remove();
1211           prev.append(cursor);
1212           assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
1213
1214           if (special)
1215             {
1216               // in this case we have to start removing the last delimimeter.
1217               // It means that we return in a situation where the user has not entirely 
1218               // inserted the delimited argument. So, we have to decrement frame.pos of 
1219               // two units: the delimiter and the actual argument
1220               std::string last_del = frame.entry.pattern[frame.pos - 1].value;
1221               frame.pos = frame.pos - 2;
1222               return "\\" + last_del;
1223             }
1224           else
1225             {
1226               // these 3 lines of code update the member pos.
1227               unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
1228               assert(sequence_length);
1229               frame.pos = frame.pos - sequence_length - 1;
1230               
1231               // since it's a graphical deletion, we have to remove the current preceding element.
1232               // We don't invoke the drop_prev(), because a do_drop_phantom_group is more general.
1233               return do_drop_phantom_group(special);
1234             }
1235         }
1236       else
1237         {
1238           // the prev is not a delimited argument, so we have to try to remove it. 
1239           // We "try", because the prev might be something that 
1240           // a simple deletion cannot remove completely
1241           return drop_prev(special);
1242         }
1243     }
1244
1245 }
1246
1247 std::string
1248 TPushParser::do_drop_groupId(bool special)
1249 {
1250   // if we are here, the cursor's parent is a group with Id
1251   assert(cursor.parent() && cursor.parent().isG() && cursor.parent().hasId());
1252   TNode parent = cursor.parent();
1253
1254   // we have to take different actions based on if the cursor has a preceding 
1255   // element or not
1256   TNode prev = cursor.prev();
1257   if (prev)
1258     {
1259       // the cursor has a preceding element, so we try to remove it
1260       if (special) return drop_prev(special);
1261       else
1262         {
1263           std::string str = drop_prev(special);
1264
1265           // We control if the group has to be removed, because the cursor 
1266           // might be the only element of the group.
1267           // But we have to be careful, because drop_prev could change the TML tree 
1268           // more than we think...parent could no longer exist! 
1269           parent = cursor.parent();
1270           if ((parent.first() == cursor) && parent.isG() && parent.hasId())
1271             rgreplace_father();
1272       
1273           return str;
1274         }
1275     }
1276   else
1277     {
1278       // the cursor has no preceding elements, so we have to remove the 
1279       // group.
1280       if (special)
1281         {
1282           parent.replace(cursor);
1283           return "";
1284         }
1285       else
1286         {
1287           rgreplace_father();
1288           // we have to re-start the process, because it' a graphical deletion
1289           return do_drop(special);
1290         }
1291     }
1292
1293 } // end of method do_drop_groupId()
1294
1295 std::string
1296 TPushParser::do_drop_phantom_group(bool special)
1297 {
1298   // if we are here, the cursor MUST be a child of a 
1299   // phantom group.
1300   assert(cursor.parent() && cursor.parent().isG() && !cursor.parent().hasId());
1301
1302   TNode parent = cursor.parent();
1303
1304   // now we have to control if the cursor has a preceding element or not
1305   TNode prev = cursor.prev();
1306   if (prev)
1307     {
1308       if (parent.parent() && parent.parent().isC())
1309         {
1310           // there is a frame in the stack
1311           assert(!frames.empty());
1312           if (frames.top().entry.pattern.size())
1313             {
1314               Frame& frame = frames.top();
1315               if (special)
1316                 {
1317                   // we are in a delimited argument. If the user has inserted a proper subset of the 
1318                   // delimiters'sequence, we start to remove the previous delimiter. Start to remove 
1319                   // a delimiter means that that delimiter must be removed from the count of inserted delimiters.
1320                   // It means that we have to decrement the member pos.
1321                   if (frame.entry.pattern[frame.pos].category != TToken::PARAMETER)
1322                     {
1323                       std::string del = frame.entry.pattern[frame.pos].value;
1324                       frame.pos--;
1325                       return "\\" + del;
1326                     }
1327                 }
1328               else
1329                 {
1330                   // we are in a delimited argument. If the user has inserted a proper subset of the delimiters'sequence, 
1331                   // we have to remove the portion the user has inserted.
1332                   while (frame.entry.pattern[frame.pos].category != TToken::PARAMETER) frame.pos--;
1333                 }
1334             }
1335         }
1336       
1337       // the cursor has a preceding element, so we try to remove it
1338       std::string str = drop_prev(special);
1339
1340       if (special) return str;
1341       else
1342         {
1343           // now we have to control the parent, to handle the case of primes. But we have returned from a drop_prev(), which
1344           // could change the TML tree. So not asssuming that cursor's parent is unchanged is convenient.
1345           parent = cursor.parent();
1346           if (parent.isG() && !parent.hasId() && (parent.size() == 1) && parent.parent().isSp())
1347             {
1348               // in this case the drop_prev has removed the only element preceding the cursor.
1349               // Since the phantom group is an sp's child, the user has removed all \' in the 
1350               // phantom group.
1351               // Now we have some possibilities:
1352               //   - we can replace the phantom group with the cursor, giving the user the chance to insert a new 
1353               //     exponent
1354               //   - we can remove the phantom group and the sp element, recreating the state before the user inserted the first
1355               //     prime.
1356               // At the moment we implement the second one.
1357               assert(parent.parent().size() == 2);
1358               TNode gparent = parent.parent();
1359               TNode base = gparent.first();
1360               cursor.remove();
1361               parent.remove();
1362               gparent.replace(base);
1363               // now base's parent is no more gparent
1364               base.parent().append(cursor);
1365           
1366               return str;
1367             }
1368           else if (parent.isG() && !parent.hasId() && parent.parent().isSp())
1369             {
1370               // in this case we have to place the cursor after the sp element
1371               cursor.remove();
1372               assert(parent.parent().parent());
1373               parent.parent().parent().append(cursor);
1374               return str;
1375             }
1376           else return str;
1377         }
1378     }
1379   else
1380     {
1381       // in this case the cursor is the only element of the phantom group,
1382       // so we have to remove it. But, a phantom group has a special role, 
1383       // so we have to control the grand father of the cursor.
1384       TNode gfather = parent.parent();
1385       if (!gfather)
1386         {
1387           // If here, the TML tree is in an inconsistent state
1388           logger.error("TML tree in a inconsistent state");
1389           return "";
1390         }
1391       else if (gfather.isC())
1392         {
1393           // in this case the phantom group is child of a MACRO.
1394           // We have to control the nature of this MACRO.
1395           assert(!frames.empty());
1396           Frame& frame = frames.top();
1397
1398           // this variable is useful in a special deletion
1399           std::string macro_name = gfather.nameC();
1400           
1401           if (frame.entry.leftOpen && frame.entry.rightOpen)
1402             {
1403               // in this case, the cursor'parent is in the second and last child 
1404               // of the MACRO. We can assert that the grand father has two 
1405               // children, which are both phantom groups
1406               assert(gfather.size() == 2);
1407               assert((gfather.last() == parent) && (gfather.first().isG() && !gfather.first().hasId()));
1408               assert(frame.pos == 0);
1409               
1410               TNode ggfather = gfather.parent();
1411               assert(ggfather);
1412               cursor.remove();
1413               parent.remove();
1414               // we have to replace the gfather with the elements of its first child, but this group may have no 
1415               // children.
1416               if (gfather.first().size())
1417                 {
1418                   gfather.replace(gfather.first().first(), TNode());
1419                   ggfather.append(cursor);
1420                 }
1421               else
1422                 {
1423                   // in this case, the MACRO has to be replaced with the cursor
1424                   gfather.first().remove();
1425                   gfather.replace(cursor);
1426                 }
1427               // now we have the situation preceding the insertion of the leftOpen and rightOpen MACRO.
1428               // this MACRO no longer exists.
1429               frames.pop();
1430
1431               if (special) return "\\" + macro_name;
1432               else
1433                 {
1434                   // to give a standard behavior to the graphical deletion, we call the do_drop.
1435                   return do_drop(special);
1436                 }
1437             }
1438           else if (frame.entry.rightOpen)
1439             {
1440               // the user has inserted a rightOpen MACRO, and now, this MACRO has no children (excluding the 
1441               // phantom group), so we remove the MACRO. 
1442               // We can assert that cursor's parent is the only child of the MACRO
1443               assert(gfather.size() == 1);
1444               assert(frame.pos == 0);
1445               cursor.remove();
1446               parent.remove();
1447               gfather.replace(cursor);
1448               
1449               // now we have the situation preceding the rightOpen MACRO, so we have to pop the frame
1450               frames.pop();
1451
1452               if (special) return "\\" + macro_name;
1453               else
1454                 {
1455                   // to give a standard behavior to the graphical deletion, we call the do_drop.
1456                   return do_drop(special);
1457                 }
1458               
1459             }
1460           else if (frame.entry.leftOpen)
1461             {
1462               // this situation will never occur.
1463               logger.error("the parser has generated a wrong TML tree");
1464               return "";
1465             }
1466           else if (!frame.entry.pattern.empty())
1467             {
1468               // the MACRO accepts arguments, and the phantom group in which 
1469               // the cursor is, rappresents a delimited argument.
1470               // We have to control if the cursor's parent has a preceding element, 
1471               // or not.
1472               TNode uncle = parent.prev();
1473               if (!uncle)
1474                 {
1475                   // the parent is the only element of the MACRO. 
1476                   // we can assert that frame.pos == 0.
1477                   // In this case we can replace the MACRO with the cursor
1478                   assert(frame.pos == 0);
1479                   cursor.remove();
1480                   parent.remove();
1481                   gfather.replace(cursor);
1482                   frames.pop();
1483
1484                   if (special) return "\\" + macro_name;
1485                   else
1486                     {
1487                       // once we have replaced the empty macro with the cursor, we can remove
1488                       // something else
1489                       return do_drop(special);
1490                     }
1491                 }
1492               else
1493                 {
1494                   // the parent has a preceding element. Now we have 
1495                   // to control if the uncle is a delimited argument or not.
1496                   if (uncle.isG() && !uncle.hasId())
1497                     {
1498                       // cursor's uncle is a delimited argument
1499                       cursor.remove();
1500                       parent.remove();
1501                       uncle.append(cursor);
1502                       if (special)
1503                         {
1504                           // we have to start removing the last delimiter of the delimited 
1505                           // argument.
1506                           std::string last_del = frame.entry.pattern[frame.pos - 1].value;
1507                           frame.pos = frame.pos - 2;
1508                           return "\\" +  last_del; 
1509                         }
1510                       else
1511                         {
1512                           // the  uncle is a delimited argument. So we have to ideally
1513                           // remove the sequence of delimiters.
1514                           assert(frame.pos > 1);
1515                           unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
1516                           assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
1517                           assert(sequence_length);
1518                           // sequence_length is the length of the delimiters sequence which separates
1519                           // the current parameter and the previous parameter
1520                           frame.pos = frame.pos - sequence_length - 1;
1521
1522                           // once removed the sequnce of delimiters, we can start to remove the actual 
1523                           // parameter. We can call the do_drop_phantom_group() because a delimited argument
1524                           // is always a phantom group's child
1525                           return do_drop_phantom_group(special);
1526                         }
1527                     }
1528                   else
1529                     {
1530                       // the uncle is a not delimited argument, so we try to remove it.
1531                       cursor.remove();
1532                       parent.replace(cursor);
1533                       parent = cursor.parent(); // we update the parent (it should be the MACRO)
1534                       assert(parent.isC());
1535
1536                       // now we try to remove the uncle (now it' the preceding element)
1537                       return drop_prev(special);
1538                     }
1539                 } // this is the else's end, that handles the case in which an uncle exists
1540             } // end of if (!frame.entry.pattern.empty())
1541           else
1542             {
1543               // the entry has no arguments, is not rightOpen and is not leftOpen.
1544               logger.error("TML tree in a strange state");
1545               return "";
1546             }
1547         } // end of if (gfather.isC())
1548       else if (gfather.is("cell"))
1549         {
1550           // A table is a control sequence, so there is a frame in the stack
1551           assert(!frames.empty());
1552           assert(frames.top().pos == 0);
1553           assert(frames.top().entry.table == 1);
1554           
1555           // a cell MUST be a row's child, which in turn is a table's child 
1556           assert(gfather.parent() && gfather.parent().is("row") && gfather.parent().parent());
1557
1558           // this variable is useful to handle the special deletion
1559           std::string table_name = gfather.parent().parent().nameC();
1560           
1561           TNode row = gfather.parent();
1562           
1563           // in this case the cell has no element, so the user wants to delete this cell.
1564           TNode prev_cell = gfather.prev();
1565           cursor.remove();
1566           parent.remove();
1567           gfather.remove();
1568           // now the cell no longer exists
1569
1570           if (!prev_cell)
1571             {
1572               // in this case, the cell was the only cell in the row.
1573               // So, we assume that the user wants to delete the entire row.
1574               TNode table = row.parent();
1575               TNode prev_row = row.prev();
1576               row.remove();
1577               
1578               if (!prev_row)
1579                 {
1580                   if (special)
1581                     {
1582                       // Since there was a cell (and a row), the user has typed a "{" to 
1583                       // We ideally remove this character.
1584                       table.append(cursor);
1585                       return "";
1586                     }
1587                   else
1588                     {
1589                       // the row was the only child of the table. 
1590                       // so we have to delete the entire table
1591                       assert(table.parent());
1592                       TNode parent_table = table.parent();
1593                       table.remove();
1594                       frames.pop();
1595                       parent_table.append(cursor);
1596                       return "";
1597                     }
1598                 }
1599               else
1600                 {
1601                   // there are other rows (one or more)
1602                   assert(prev_row.is("row"));
1603                   assert(prev_row.last());
1604                   TNode last_cell = prev_row.last();
1605                   assert(last_cell.is("cell"));
1606                   assert(last_cell.size() == 1);
1607                   assert(last_cell.first().isG() && !last_cell.first().hasId());
1608                   last_cell.first().append(cursor);
1609                   // Since cells and rows are separated by spaces and CRs 
1610                   // (and the user can see this spaces and CRs), a special deletion 
1611                   // is equivalent to a normal deletion
1612                   return "";
1613                 }
1614             } // end of if (!prev_cell)
1615           else
1616             {
1617               // being here means that there is a previous cell,
1618               // so we append the cursor to group.
1619               assert(prev_cell.size() == 1);
1620               assert(prev_cell.first().isG() && !prev_cell.first().hasId());
1621               prev_cell.first().append(cursor);
1622               return "";
1623             }
1624         } // end of if (gfather.is("cell"))
1625       else if (gfather.isSp())
1626         {
1627           // we cannot be here because a phantom group can be a Sp child only 
1628           // in two cases. If the user has typed somethong like:
1629           // $^
1630           // the cursor is not phantom group's child.
1631           // If the user has typed somethong like
1632           // ..''
1633           // In this case the sequence of ' is placed in a phantom group, 
1634           // which becomes the exponent of the script. But, the cursor is 
1635           // always outside the phantom group
1636           logger.error("TML tree in a strange state");
1637           return "";
1638         }
1639       else if (gfather.is("math"))
1640         {
1641           // in this case we ignore the user's will of deleting
1642           // but we could also decide to remove the math mode.
1643           logger.warning("nothing to delete");
1644           return "";
1645         }
1646       else
1647         {
1648           // cursor's grand father is undefined
1649           logger.error("TML tree is in an unknown state");
1650           return "";
1651         }
1652     } // end of the else of the if (prev)
1653
1654 }
1655
1656
1657 std::string
1658 TPushParser::do_drop(bool special)
1659 {
1660   // we have to handle the case in wich the cursor has a parent or not
1661   if (!cursor.parent())
1662     {
1663       // it's not a good situation...at the moment we do not take actions
1664       logger.error("TML tree not well structured");
1665       return "";
1666     }
1667   else
1668     {
1669       // a parent exists. We have to take differnt actions, based on the nature of 
1670       // the parent
1671       TNode parent = cursor.parent();
1672       if (parent.is("math"))
1673         {
1674           // we ca do two thing...we can remove the math mode (it implies controlling the display attribute), we can do nothing
1675           // At the moment, the user's will of deleting is simply ignored
1676           logger.warning("nothing to delete");
1677           return "";
1678         }
1679       else if (parent.isG())
1680         {
1681           // the cursor's parent is a group. We have to control if it's a phantom group or not
1682           if (parent.hasId())
1683             {
1684               return do_drop_groupId(special);
1685             }
1686           else
1687             {
1688               return do_drop_phantom_group(special);
1689             }
1690         } // end of parent is group
1691       else if (parent.isC())
1692         {
1693           return do_drop_macro(special);
1694         } // end of parent is a MACRO
1695       else if (parent.isSp() || parent.isSb())
1696         {
1697           return do_drop_script(special);
1698         } // end of parent is sp or sb
1699     } // end of the else which consider the case in which parent exists
1700   
1701 } // end of method do_drop
1702
1703 bool
1704 TPushParser::process(const TToken& token)
1705 {
1706   switch (token.category)
1707     {
1708     case TToken::BEGIN: return do_begin();
1709     case TToken::END: return do_end();
1710     case TToken::SHIFT: return do_shift();
1711     case TToken::ALIGN: return do_align();
1712     case TToken::EOL: return do_eol();
1713     case TToken::PARAMETER: return do_parameter(token.value);
1714     case TToken::SUPERSCRIPT: return do_superscript();
1715     case TToken::SUBSCRIPT: return do_subscript();
1716     case TToken::IGNORABLE_SPACE: return do_ignorablespace(token.value);
1717     case TToken::SPACE: return do_space(token.value);
1718     case TToken::LETTER: return do_letter(token.value);
1719     case TToken::DIGIT: return do_digit(token.value);
1720     case TToken::OTHER: return do_other(token.value);
1721     case TToken::ACTIVE: return do_active(token.value);
1722     case TToken::COMMENT: return do_comment();
1723     case TToken::CONTROL: return do_control(token.value);
1724     }
1725 }
1726
1727 void
1728 TPushParser::push(const TToken& token)
1729 {
1730   TNode parent = cursor.parent();
1731   // If the cursor has no parent then it is detached from the editing
1732   // tree, which means this token will be ignored
1733
1734   if (parent)
1735     // If the parent is a phantom group and the grand-parent is a
1736     // control sequence, there are two cases:
1737     // a. we are parsing a delimited argument of a entry
1738     // b. we are parsing a side of a right- or left-open entry
1739     if (parent.isG() && !parent.hasId() && parent.parent().isC())
1740       {
1741         // There must be an open frame, for the grand-parent is a control sequence
1742         assert(!frames.empty());
1743         Frame& frame = frames.top();
1744         if (!frame.entry.pattern.empty())
1745           {
1746             // The entry pattern is not empty. By our conventions this means
1747             // the entry cannot be open at either end, hence we are parsing
1748             // a delimited argument
1749             assert(frame.pos + 1 < frame.entry.pattern.size());
1750             assert(frame.entry.pattern[frame.pos + 1].category != TToken::PARAMETER);
1751             if (frame.entry.pattern[frame.pos + 1] == token)
1752               {
1753                 // The token matches with a delimiter of the argument, 
1754                 // hence we increment the frame.pos
1755                 frame.pos++;
1756
1757                 if (frame.entry.lastDelimiter(frame.pos))
1758                   {
1759                     // this delimiter is the last one for the argumet, 
1760                     // so the argument is completed
1761                     cursor.remove();
1762                     advance(parent);
1763                   }
1764               }
1765             else
1766               {
1767                 // Delimiter mismatch.
1768                 if (frame.entry.pattern[frame.pos].category != TToken::PARAMETER)
1769                   {
1770                     // in this case, there is a sequence of delimiters that delimitates
1771                     // the argument, and the user has correctly inserted a portion of this 
1772                     // sequence, but now has inserted a wrong delimiter.
1773                     // Here, there are some possibilities:
1774                     //   - ignore the token, and wait for the correct delimiter
1775                     //   - ignore the token, wait for the correct delimiter and emit an error
1776                     // At the moment, we implement the second possibily
1777                     logger.warning("it's not the correct delimiter...you have to type '" + frame.entry.pattern[frame.pos + 1].value + "'");
1778                   }
1779                 else
1780                   {
1781                     // in this case, the sequence of delimiters is composed of one 
1782                     // delimiter. It means that we have to process the token
1783                     process(token);
1784                   }
1785               }
1786           }
1787         else
1788           {
1789             // The entry pattern is empty, hence we are parsing a right-open
1790             // entry. What happens if we actually are in the left side?
1791             // This could happen only when re-editing an entered expression
1792             // We'll see...
1793             assert(frame.entry.rightOpen);
1794             process(token);
1795           }
1796       }
1797     else if (parent.isC())
1798       {
1799         // We are parsing a non-delimited argument entry
1800         // or a fixed token
1801         Frame& frame = frames.top();
1802         assert(frame.pos < frame.entry.pattern.size());
1803
1804         if (frame.entry.pattern[frame.pos].category == TToken::PARAMETER)
1805           {
1806             // As by the TeX parsing rules of undelimited parameters,
1807             // empty spaces are ignored
1808             if ((token.category != TToken::SPACE) && (token.category != TToken::IGNORABLE_SPACE)) process(token);
1809           }
1810         else if (frame.entry.pattern[frame.pos] == token)
1811           {
1812             // The token has been accepted
1813             frame.pos++;
1814             if (frame.pos < frame.entry.pattern.size() &&
1815                 frame.entry.paramDelimited(frame.pos))
1816               {
1817                 // If the next is a delimited argument we have to place
1818                 // the phantom group with the cursor inside
1819                 TNode g = doc.createG();
1820                 cursor.replace(g);
1821                 g.append(cursor);
1822               }
1823             else
1824               {
1825                 cursor.remove();
1826                 advance(parent);
1827               }
1828           }
1829         else
1830           {
1831             // There is a mismatch. Emit an error and ignore the token?
1832             logger.warning("ignored token: " + token.value);
1833           }
1834       }
1835     else
1836       process(token);
1837   else
1838     {
1839       logger.warning("ignored token");
1840     }
1841
1842   if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1843 }
1844
1845 std::string
1846 TPushParser::drop(bool special)
1847 {
1848   std::string str = do_drop(special);
1849   if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1850   return str;
1851 }
1852
1853 void
1854 TPushParser::advance(const TNode& node)
1855 {
1856   assert(node);
1857   
1858   if (!node.parent())
1859     {
1860       // this is an error
1861       logger.error("wrong TML tree");
1862     }
1863   else if (node.parent().isG())
1864     {
1865       TNode next = node.next();
1866       if (next) next.insert(cursor);
1867       else node.parent().append(cursor);
1868     }
1869   else if (node.parent().isC())
1870     {
1871       assert(!frames.empty());
1872       if ((frames.top().pos + 1 == frames.top().entry.pattern.size()) || (frames.top().entry.pattern.empty()))
1873         {
1874           // we are here when we have a right open macro, or the inserted element is the last one
1875           if (frames.top().entry.rightOpen)
1876             {
1877               // we have to remove the frame from the stack
1878               frames.pop();
1879               advance(node.parent().parent());
1880             }
1881           else
1882             {
1883               frames.pop();
1884               advance(node.parent());
1885             }
1886         }
1887       else if (frames.top().entry.paramDelimited(frames.top().pos + 1))
1888         {
1889           // the next argument is delimited, so we have to create a phantom group
1890           TNode g = doc.createG();
1891           g.append(cursor);
1892           node.parent().append(g);
1893           frames.top().pos++;
1894         }
1895       else
1896         {
1897           // the next argumet is not delimited, so we have to append the cursor
1898           // to the MACRO
1899           node.parent().append(cursor);
1900           frames.top().pos++;
1901         }
1902     }
1903   else advance(node.parent());
1904 }
1905
1906 void
1907 TPushParser::setCursorHint(const std::string& c)
1908 {
1909   if (cursor["val"] != c)
1910     {
1911       cursor["val"] = c;
1912       if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1913     }
1914 }
1915
1916 bool
1917 TPushParser::hideCursor()
1918 {
1919   if (hiddenCursor++ == 0)
1920     {
1921       cursor["visible"] = "0";
1922       if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1923       return true;
1924     }
1925   else
1926     return false;
1927 }
1928
1929 bool
1930 TPushParser::showCursor()
1931 {
1932   if (hiddenCursor > 0 && --hiddenCursor == 0)
1933     {
1934       cursor["visible"] = "1";
1935       if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1936       return true;
1937     }
1938   else
1939     return false;
1940 }
1941
1942 bool
1943 TPushParser::thaw()
1944 {
1945   if (APushParser::thaw() && factory && doc.dirtyNode())
1946     {
1947       factory->documentModified(doc);
1948       return true;
1949     }
1950   else
1951     return false;
1952 }