]> matita.cs.unibo.it Git - helm.git/blob - helm/DEVEL/mathml_editor/src/TPushParser.cc
Added some controls to handle the case in which the user type an unexpected '{'
[helm.git] / helm / DEVEL / mathml_editor / src / TPushParser.cc
1
2 #include "ALogger.hh"
3 #include "TPushParser.hh"
4 #include "AMathMLFactory.hh"
5
6 TPushParser::TPushParser(ALogger& l, const TDictionary& d) : APushParser(l), dictionary(d)
7 {
8   init();
9 }
10
11 TPushParser::TPushParser(ALogger& l, AMathMLFactory& f, const TDictionary& d) : APushParser(l, f), dictionary(d)
12 {
13   init();
14 }
15
16 void
17 TPushParser::init()
18 {
19   nextId = 1;
20   cursor = doc.create("cursor");
21   cursor["id"] = "I0";
22   doc.clearDirty();
23   doc.root().append(cursor);
24   logger.verbosity(ALogger::Debug);
25 }
26
27 TPushParser::~TPushParser()
28 {
29 }
30
31 std::string
32 TPushParser::PRIME() const
33 {
34   const TDictionary::Entry entry = dictionary.find("prime");
35   if (entry.cls == TDictionary::OPERATOR) return entry.value;
36   else return "?";
37 }
38
39 void
40 TPushParser::do_begin()
41 {
42   TNode parent = cursor.parent();
43   if (parent.isC() && dictionary.find(parent.nameC()).table)
44     {
45       TNode row = doc.create("row");
46       TNode cell = doc.create("cell");
47       TNode g = doc.createG();
48       row.append(cell);
49       cell.append(g);
50       g.append(cursor);
51       parent.append(row);
52     }
53   else
54     {
55       TNode g = doc.createG(nextId++);
56       cursor.replace(g);
57       g.append(cursor);
58     }
59 }
60
61 void
62 TPushParser::do_end()
63 {
64   TNode parent = cursor.parent();
65   if (parent && parent.isG() && parent.hasId())
66     {
67       // normal closing brace for an explicitly open group
68       cursor.remove();
69       advance(parent);
70     }
71   else if (parent && parent.isG() && parent.parent() && parent.parent().is("cell"))
72     {
73       assert(!frames.empty());
74       // closing brace for a structure in which & or \cr have been used
75       TNode row = parent.parent().parent();
76       assert(row && row.is("row"));
77       TNode table = row.parent();
78       assert(table);
79       advance(row);
80     }
81   else if (parent && parent.isG() && !parent.hasId() && parent.parent() && !parent.parent().is("math"))
82     {
83       // closing brace for a right-open macro (like \over)
84       // It's not sure that we want to exit from a phantom group with a 
85       // '}', because, to enter in the phantom group the user didn't insert a 
86       // '{'.
87       cursor.remove();
88       advance(parent);
89     }
90   else
91     {
92       // In this case, there is a redundant '}', so I think we can ignore it and 
93       // emit an error
94       logger.warning("There is so no corresponding'{'");
95       //assert(0);
96     }
97 }
98
99 void
100 TPushParser::do_shift()
101 {
102   TNode parent = cursor.parent();
103   assert(parent);
104   if (parent.is("tex"))
105     {
106       TNode math = doc.create("math", nextId++);
107       TNode g = doc.createG();
108       cursor.replace(math);
109       math.append(g);
110       g.append(cursor);
111     }
112   else if (parent.isG() && !parent.hasId() && parent.parent() && parent.parent().is("math"))
113     {
114       if (cursor.prev())
115         {
116           // there is something before the cursor, hence this is the
117           // closing math shift
118           if (parent.parent()["display"] != "1")
119             {
120               // one math shift is enough to close it
121               cursor.remove();
122             }
123           else
124             {
125               // we need two closing math shifts
126               //cursor.remove(); ??
127               parent.parent().append(cursor);
128             }
129         }
130       else if (parent.parent()["display"] != "1")
131         {
132           // there is nothing before the cursor, and the math is not
133           // in display mode, so this must be a double math shift
134           parent.parent()["display"] = "1";
135         }
136       else
137         {
138           parent.parent().append(cursor);
139         }
140     }
141   else if (parent.is("math"))
142     {
143       cursor.remove();
144     }
145   else
146     {
147       // In TeX, the user can type
148       //   $a-b\over2a+b$
149       // In this case, '$' character is correct.
150       // I don't think so, if user types
151       //   $12+{a-b\over2a+b$
152       // because the group is not closed with a '}'
153       logger.error("parser: math shift");
154     }
155 }
156
157 void
158 TPushParser::do_align()
159 {
160   TNode parent = cursor.parent();
161   if (parent && parent.isG() && parent.hasId())
162     {
163       // alignment tab used for the first time inside a group
164       TNode row = doc.create("row");
165       TNode cell = doc.create("cell");
166       TNode g = doc.createG();
167       row.append(cell);
168       cell.append(g);
169       g.append(parent.first(), cursor);
170     }
171   else if (parent && parent.isG() && parent.parent().is("cell"))
172     {
173       // alignment tab used within a cell
174       TNode oldCell = parent.parent();
175       assert(oldCell && oldCell.is("cell"));
176       TNode row = oldCell.parent();
177       assert(row && row.is("row"));
178       TNode cell = doc.create("cell");
179       if (oldCell.next()) oldCell.next().insert(cell);
180       else row.append(cell);
181       TNode g = doc.createG();
182       cell.append(g);
183       g.append(cursor);
184     }
185   else
186     {
187       logger.error("alignment tab used outside matrix");
188     }
189 }
190
191 void
192 TPushParser::do_eol()
193 {
194   //if (cursor.parent()) cursor.remove();
195 }
196
197 void
198 TPushParser::do_parameter(const std::string& p)
199 {
200   // ???
201 }
202
203 void
204 TPushParser::do_subscript()
205 {
206   TNode parent = cursor.parent();
207   if (parent.isG())
208     {
209       TNode prev = cursor.prev();
210       if (!prev)
211         {
212           TNode elem = doc.create("sb", nextId++);
213           TNode g = doc.createG();
214           cursor.replace(elem);
215           elem.append(g);
216           elem.append(cursor);
217         }
218       else
219         {
220           TNode elem = doc.create("sb", nextId++);
221           prev.replace(elem);
222           elem.append(prev);
223           elem.append(cursor);
224         }
225     }
226   else if (parent.isSb() && cursor == parent[1])
227     {
228       if (parent["under"] == "1") logger.error("already under");
229       else parent["under"] = "1";
230     }
231 }
232
233 void
234 TPushParser::do_superscript()
235 {
236   TNode parent = cursor.parent();
237   if (parent.isG())
238     {
239       TNode prev = cursor.prev();
240       if (!prev)
241         {
242           TNode elem = doc.create("sp", nextId++);
243           TNode g = doc.createG();
244           cursor.replace(elem);
245           elem.append(g);
246           elem.append(cursor);
247         }
248       else
249         {
250           TNode elem = doc.create("sp", nextId++);
251           prev.replace(elem);
252           elem.append(prev);
253           elem.append(cursor);
254         }
255     }
256   else if (parent.isSp() && cursor == parent[1])
257     {
258       if (parent["over"] == "1") logger.error("already over");
259       else parent["over"] = "1";
260     }
261 }
262
263 void
264 TPushParser::do_space(const std::string&)
265 {
266   // ? may be used to distinguish tokens in some mode?
267 }
268
269 void
270 TPushParser::do_letter(const std::string& s)
271 {
272   //TNode parent = cursor.parent();
273   TNode elem = doc.createI(s, nextId++);
274   cursor.replace(elem);
275   advance(elem);
276 }
277
278 void
279 TPushParser::do_digit(const std::string& s)
280 {
281   TNode parent = cursor.parent();
282   TNode prev = cursor.prev();
283   if (prev && parent.isG() && prev.is("n"))
284     {
285       TNode elem = doc.createN(prev.value() + s, nextId++);
286       prev.replace(elem);
287     }
288   else
289     {
290       TNode elem = doc.createN(s, nextId++);
291       cursor.replace(elem);
292       advance(elem);
293     }
294 }
295
296 bool
297 TPushParser::isPrimes(const TNode& node) const
298 {
299   assert(node);
300   return node.isG() && node.last() && node.last().is("o") && node.last()["val"] == PRIME();
301 }
302
303 void
304 TPushParser::do_apostrophe()
305 {
306   if (cursor.parent() && cursor.parent().isG())
307     {
308       if (TNode prev = cursor.prev())
309         {
310           if (prev.isSp() && prev[1] && isPrimes(prev[1]))
311             prev[1].append(doc.createO(PRIME(), nextId++));
312           else if (prev.isSb() && prev[0] &&
313                    prev[0].isSp() && prev[0][1] &&
314                    isPrimes(prev[0][1]))
315             prev[0][1].append(doc.createO(PRIME(), nextId++));
316           else
317             {
318               TNode elem = doc.create("sp");
319               TNode g = doc.createG();
320               prev.replace(elem);
321               elem.append(prev);
322               elem.append(g);
323               g.append(doc.createO(PRIME(), nextId++));
324             }
325         }
326       else
327         {
328           // is it an error?
329           logger.error("parser: you have to type one identifier before the  ''");
330         }
331     }
332   else
333     {
334       // error ??
335     }
336 }
337
338 void
339 TPushParser::do_other(const std::string& s)
340 {
341   switch (s[0])
342     {
343     case '\'':
344       do_apostrophe();
345       break;
346     default:
347       /*cout << "TPushParser::do_other " << s << endl;
348       cout << "DOCUMENT: " << static_cast<GdomeNode*>(cursor.element().get_ownerDocument()) << endl;*/
349       TNode elem = doc.createT("o", s, nextId++);
350       cursor.replace(elem);
351       advance(elem);  
352       break;
353     }
354 }
355
356 void
357 TPushParser::do_active(const std::string&)
358 {
359   // ??? space?
360 }
361
362 void
363 TPushParser::do_comment()
364 {
365   // ???
366 }
367
368 void
369 TPushParser::do_cr()
370 {
371   TNode parent = cursor.parent();
372   if (parent && parent.isG() &&
373       parent.parent() && parent.parent().is("cell") &&
374       parent.parent().parent() && parent.parent().parent().is("row"))
375     {
376       TNode oldRow = parent.parent().parent();
377       assert(oldRow);
378       TNode table = oldRow.parent();
379       assert(table);
380       TNode row = doc.create("row");
381       TNode cell = doc.create("cell");
382       TNode g = doc.createG();
383       if (oldRow.next()) oldRow.next().insert(row);
384       else table.append(row);
385       row.append(cell);
386       cell.append(g);
387       g.append(cursor);
388     }
389 }
390
391 void
392 TPushParser::do_control(const std::string& name)
393 {
394   if (name == "cr") do_cr();
395   else
396     {
397       TNode parent = cursor.parent();
398       const TDictionary::Entry& entry = dictionary.find(name);
399       switch (entry.cls)
400         {
401         case TDictionary::IDENTIFIER:
402           {
403             TNode t = doc.createI(entry.value, nextId++);
404             t["name"] = name;
405             cursor.replace(t);
406             advance(t);
407           }
408           break;
409         case TDictionary::OPERATOR:
410           {
411             TNode t = doc.createO(entry.value, nextId++);
412             t["name"] = name;
413             cursor.replace(t);
414             advance(t);
415           }
416           break;
417         case TDictionary::NUMBER:
418           {
419             TNode t = doc.createN(entry.value, nextId++);
420             t["name"] = name;
421             cursor.replace(t);
422             advance(t);
423           }
424           break;
425         case TDictionary::MACRO:
426           {
427             TNode m = doc.createC(name, nextId++);
428             cursor.replace(m);
429             
430             if (entry.leftOpen && entry.rightOpen)
431               {
432                 assert(entry.pattern.empty());
433                 assert(parent.isG());
434                 TNode g1 = doc.createG();
435                 g1["left-open"] = "1";
436                 g1.append(parent.first(), m);
437                 m.append(g1);
438                 TNode g2 = doc.createG();
439                 g2.append(cursor);
440                 m.append(g2);
441                 frames.push(Frame(entry));
442               }
443             else if (entry.leftOpen)
444               {
445                 assert(parent.isG());
446                 TNode g = doc.createG();
447                 g["left-open"] = "1";
448                 g.append(parent.first(), m);
449                 m.append(g);
450                 advance(m);
451               }
452             else if (entry.rightOpen)
453               {
454                 assert(entry.pattern.empty());
455                 assert(parent.isG());
456                 TNode g = doc.createG();
457                 g.append(cursor);
458                 m.append(g);
459                 frames.push(Frame(entry));
460               }
461             else if (!entry.pattern.empty())
462               {
463                 if (parent.isG())
464                   {
465                     frames.push(Frame(entry));
466
467                     if (entry.paramDelimited(0))
468                       {
469                         TNode g = doc.createG();
470                         m.append(g);
471                         g.append(cursor);
472                       }
473                     else
474                       m.append(cursor);
475                   }
476                 else
477                   {
478                     // error, but we could handle this very easily
479                     logger.error(" parser:but we could handle this easily");
480                   }
481               }
482             else advance(m);
483           }
484           break;
485         case TDictionary::UNDEFINED:
486           {
487             logger.error("parser: using undefined macro " + name);
488             TNode m = doc.createC(name, nextId++);
489             cursor.replace(m);
490             advance(m);
491           }
492           break;
493         default:
494           assert(0);
495         }
496     }
497 }
498
499 void
500 TPushParser::gdelete_prev_token()
501 {
502   assert(cursor.prev());
503   assert(cursor.parent());
504   TNode prev = cursor.prev();
505   assert(prev.is("i") || prev.is("o") || prev.is("n"));
506   
507   // the control below is designed to handle the case in which val have more than one unicode character
508   DOM::UCS4String ucs4val(prev.element().getAttribute("val"));
509   if ((ucs4val.length() <= 1) || prev.element().hasAttribute("name"))
510     {
511       cursor.remove();
512       prev.replace(cursor);
513       
514       if (cursor.parent().isC())
515         {
516           // in this case we have removed an element of a MACRO. 
517           // we can assert that this element was a non delimited argument
518           assert(!frames.empty());
519           Frame& frame = frames.top();
520           assert(frame.pos > 0);
521
522           frame.pos--;
523         }
524     }
525   else
526     {
527       ucs4val.erase(ucs4val.length() - 1, 1);
528       prev.element().setAttribute(DOM::GdomeString("val"), DOM::GdomeString(ucs4val));
529     }
530
531 }
532
533 void
534 TPushParser::gdelete_prev_script()
535 {
536   // this method delete an sp or an sb preceding the cursor
537   assert(cursor.prev());
538   assert(cursor.parent());
539   TNode prev = cursor.prev();
540   assert(prev.is("sp") || prev.is("sb"));
541   cursor.remove();
542   prev.append(cursor);
543   // i can invoke the gdelet_prev, because a sp (sb) MUST have two children
544   gdelete_prev();
545 }
546
547 void
548 TPushParser::gdelete_prev_group()
549 {
550   assert(cursor.prev() && cursor.prev().isG());
551   TNode prev = cursor.prev();
552   cursor.remove();
553   prev.append(cursor);
554   
555   // a group may have no children, so the gdelete_prev is not appropriate
556   // so this method is not equivalent to the one above
557   do_gdelete();
558 }
559
560 void
561 TPushParser::gdelete_prev_macro()
562 {
563   assert(cursor.parent());
564   assert(cursor.prev());
565   TNode prev = cursor.prev();
566   assert(prev.isC());
567   
568   const TDictionary::Entry& entry = dictionary.find(prev["name"]);
569   
570   if (!entry.defined())
571     {
572       // We can assume tha the user want to completely delete the undefined macro
573       cursor.remove();
574       prev.replace(cursor);
575     }
576   else
577     {   
578       // we start to remove a MACRO. Different actions must be taken, based on the nature 
579       // of the MACRO. In some cases, we can't remove the MACRO immediately, in other
580       // cases it's correct. In the first set of cases, we have to update the stack, pushing
581       // a frame in it with a correct value of pos, in the 
582       // second one, we must not push a frame in the stack
583       
584       if (entry.rightOpen)
585         {
586           // In this fragment of code we also handle the leftOpen && rightOpen MACRO.
587           // if the control element is rightOpen, the cursor should be placed after 
588           // the last child of the control element's last child, and than, we try to remove something.
589           // A frame MUST be pushed in the stack, because we dont' know if the following actions 
590           // will completely remove the MACRO.
591           frames.push(Frame(entry));
592
593           // Since the MACRO is rightOpen, the last child of the MACRO must be a phantom group
594           assert(prev.last().isG() && !prev.last().hasId());
595           
596           cursor.remove();
597           prev.last().append(cursor);
598           
599           // the gdelete_prev is not appropriate, because the last child of the MACRO may have no children
600           do_gdelete_phantom_group();
601         }
602       else if (entry.leftOpen)
603         {
604           // the leftOpen MACRO MUST have one and only one child, which MUST be a phantom group
605           // In this case, we do not have to push a frame in the stack, because we remove the 
606           // MACRO immediately, substituting it with the content of the phantom group.
607           // At the moment, we don't remove the last child of the phantom group, but
608           // i don't know if it's the correct behavior of the graphical deleting.
609           // To delete it, just remove the comment of the last instruction of this block 
610           // of code.
611           assert(prev.first());
612           assert(prev.first().isG());
613           assert(prev.first() == prev.last());
614           
615           TNode g = prev.first();
616           g.remove();
617           prev.replace(g.first(), TNode());
618           //do_gdelete();
619         }
620       else if (!entry.pattern.empty())
621         {
622           // we have to start to remove a MACRO which accepts arguments.
623           // If the MACRO accepts arguments, the MACRO has one or more children           
624           assert(prev.size() >= 1);
625
626           // Differnt actions must be taken, based on the nature of the last child
627           // of the MACRO. We have to distinguish the case in which it's a delimited argument
628           // frome the one in which it's a not delimited argument.
629           if (prev.last().isG() && !prev.last().hasId())
630             {
631               // the last argument of the MACRO is a delimited argumet. We ideally remove 
632               // the sequence of delimiters
633               cursor.remove();
634               prev.last().append(cursor);
635               //  we have to push a frame with a correct value of pos
636               assert(entry.previousParam(entry.pattern.size()) != entry.pattern.size());
637               
638               unsigned sequence_length = entry.pattern.size() - entry.previousParam(entry.pattern.size()) - 1;
639               unsigned p = entry.pattern.size() - sequence_length - 1;
640               // now, p is the correct value of pos, and we can push the frame.
641               frames.push(Frame(entry, p));
642             }
643           else
644             {
645               // in this case, the last child of the MACRO is not a delimited argument, so we try 
646               // to remove it, but we have to take differnt actions if the MACRO is a table with rows or not. 
647               cursor.remove();
648               if (entry.table == 1 && prev.last().is("row"))
649                 {
650                   // in this case the cursor should be appended to the group associated to 
651                   // the last cell of the last row of the table
652                   assert(prev.last().last().is("cell") && prev.last().last().first().isG());
653                   prev.last().last().first().append(cursor);
654                 }
655               else
656                 {
657                   prev.append(cursor);
658                 }
659
660               // we push a frame in the stack with a correct value of member pos.
661               // This correct value is the size of the pattern, because we have to START to delete 
662               // a MACRO. It means that all of the MACRO's arguments have been inserted.
663               frames.push(Frame(entry, entry.pattern.size()));
664
665               if (cursor.prev())
666                 {
667                   // in this case we try to remove the last child of the MACRO.
668                   gdelete_prev();
669                 }
670               else
671                 {
672                   // this block of code will never be executed, because the MACRO accepts arguments, 
673                   // so a MACRO's child MUST exist. But, we can handle this case, emitting a warning
674                   logger.warning("Parser: the TML tree is in a strange state, but a good state will be generated");
675                   do_gdelete();
676                 }
677             } // end of the else of the if (prev.last().isG() && !prev.last().hasId())
678
679         } // end of if (!entry.pattern.empty())
680       else
681         {
682           // if we are here, the MACRO preceding the cursor, is !(rightOpen ||leftOpen),
683           // and has no pattern. It means that it has no children.
684           // We can replace it with the cursor
685           assert(prev.size() == 0);
686           cursor.remove();
687           prev.replace(cursor);
688         }
689     } // end of defined MACRO
690
691 }
692
693 void
694 TPushParser::gdelete_prev()
695 {
696   // if in this function, the prev of cursor does exist, also the parent and we want a graphical deleting.
697   
698   assert(cursor.prev());
699   assert(cursor.parent());
700
701   TNode prev = cursor.prev();
702
703   if (prev.is("i") || prev.is("o") || prev.is("n"))
704     {
705       gdelete_prev_token();
706     }
707   else if (prev.isSp() || prev.isSb())
708     {
709       gdelete_prev_script();
710     }
711   else if (prev.isG())
712     {
713       gdelete_prev_group();
714     }
715   else if (prev.isC())
716     {
717       // here, we also treat the case in which the MACRO is a table
718       gdelete_prev_macro();
719     }
720   else 
721     {
722       // not handled. Future cases...
723     }
724
725 } // end of method
726
727 void
728 TPushParser::rgreplace_father(void)
729 {
730   // this method MUST only be invoked, when the cursor
731   // is the only child of a group with id. This function 
732   // replaces the group with the cursor. But if the new parent
733   // is a group with id and the cursor is the only child of the 
734   // group, the new parent is replaced...and so on.
735   // r stands for recursive, g stands for graphical.
736   assert(cursor.parent());
737   assert(cursor.parent().isG() && cursor.parent().hasId());
738
739   TNode parent = cursor.parent();
740
741   while (parent.isG() && parent.hasId() && (parent.first() == cursor))
742     {
743       parent.replace(cursor);
744       parent = cursor.parent();
745     }
746
747   if (parent.isC())
748     {
749       // in this case we have removed a MACRO's child. 
750       // So, we have to update the member pos of the frame in the stack
751       // I can assert that this MACRO accepts arguments.
752       assert(!frames.empty());
753       Frame& frame = frames.top();
754       assert(frame.pos > 0);
755       frame.pos--;
756     }
757 }
758
759 void
760 TPushParser::do_gdelete_script()
761 {
762   // If we are here, the cursor is child of a script (sp or sb) and 
763   // this means that a prev MUST exist and that there is one and only one 
764   // element preceding the cursor. The sp's (or sb's) parent 
765   // MUST NOT be a MACRO.
766   // The element preceding the cursor is the base of the script.
767
768   assert(cursor.parent() && (cursor.parent().isSp() || cursor.parent().isSb()));
769   TNode parent = cursor.parent();
770           
771   assert(parent.size() == 2);
772   assert(parent.parent() && !parent.parent().isC());
773   
774   TNode prev = cursor.prev();
775   cursor.remove();
776   if (prev.isG() /*&& !prev.hasId()*/ && (prev.size() == 0))
777     {
778       // in this case, the script's base is a group with no elements, so 
779       // we have to remove the entire MACRO, replacing it with the cursor.
780       // This situation occurs when the user type something like this
781       //   $....{}^
782       // or this 
783       //   $^
784       // or this
785       //   $...{^
786       prev.remove();
787       parent.replace(cursor);
788       
789       // if the new parent is a group with Id and the cursor is the only 
790       // element of this group, we have to remove the group. This controls are made
791       // in the method rgreplace_father().
792       if (cursor.parent().isG() && cursor.parent().hasId()) rgreplace_father();
793     }
794   else
795     {
796       // in this case, the prev has to replace the script, 
797       // and the cursor has to be placed after the prev. 
798       assert(prev.hasId());
799       parent.replace(prev);
800       prev.parent().append(cursor);
801       // now prev have a preceding element
802       assert(cursor.parent().size() > 1);
803     }
804   
805 } // end of method do_gdelet_script
806
807 void
808 TPushParser::do_gdelete_macro()
809 {
810   // If we are here, the cursor is a child of a MACRO and this means
811   // that there is an open frame for the control element
812   // and this element is closed at either side (no leftOpen no rightOpen)
813   // and the MACRO is waiting for a not delimited argument, so 
814   // we can assert that frame.entry.pattern.size() >= 1
815   assert(cursor.parent() && cursor.parent().isC());
816   TNode parent = cursor.parent();
817   
818   assert(!frames.empty());
819   Frame& frame = frames.top();
820   assert(frame.entry.pattern.size() >= 1);
821
822   // we have to take different actions, based on if a preceding element exists 
823   // or not
824   TNode prev = cursor.prev();
825   if (!prev)
826     {
827       // in this case, a prev does not exist, so the actions of deleting means 
828       // that we have to remove the MACRO. So we have to pop the stack.
829       // Being here also means that the MACRO is waiting for the first argument
830       // (which is not delimited), but we don't mind about it.
831       assert(frame.pos == 0);
832       parent.replace(cursor);
833       frames.pop();
834
835       // if the new parent is a group with Id, and has no elements other than the 
836       // cursor, we can remove it, because it' a graphical deleting
837       if (cursor.parent() && cursor.parent().isG() && cursor.parent().hasId())
838         rgreplace_father();
839       else if (cursor.parent().isC())
840         {
841           // We have assumed that a MACRO cannot be a MACRO's child. 
842           // At the moment, this assumption is valid, but in a future 
843           // it might be false.
844           assert(!frames.empty());
845           Frame& frame = frames.top();
846           assert(frame.pos > 0);
847           frame.pos--;
848         }
849     }
850   else
851     {
852       // a prev does exist, we have to control if it's a delimited argument or not.
853       if (prev.isG() && !prev.hasId())
854         {
855           // in this case, prev is a delimited argument, so we have 
856           // to ideally remove the sequence of delimiters
857           Frame& frame = frames.top();
858           assert(frame.pos > 1);
859           cursor.remove();
860           prev.append(cursor);
861           assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
862           
863           // these 3 lines of code update the member pos.
864           unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
865           assert(sequence_length);
866           frame.pos = frame.pos - sequence_length - 1;
867         }
868       else
869         {
870           // the prev is not a delimited argument, so we have to try to remove it. 
871           // We "try", because the prev might be a something that 
872           // a simple delete cannot remove completely
873           gdelete_prev();
874         }
875     }
876
877 }
878
879 void
880 TPushParser::do_gdelete_groupId()
881 {
882   // if we are here, the cursor's parent is a group with Id
883   assert(cursor.parent() && cursor.parent().isG() && cursor.parent().hasId());
884   TNode parent = cursor.parent();
885
886   // we have to take different actions based on if the cursor has a preceding 
887   // element or not
888   TNode prev = cursor.prev();
889   if (prev)
890     {
891       // the cursor has a preceding element, so we try to remove it
892       gdelete_prev();
893
894       // We control if the group has to be removed, because the cursor 
895       // might be the only element of the group.
896       if ((parent.first() == cursor) && parent.isG() && parent.hasId())
897         rgreplace_father();
898       
899     }
900   else
901     {
902       // the cursor has no preceding elements, so we have to remove the 
903       // group.
904       rgreplace_father();
905       
906       // we have to re-start the process, because it' a graphical delete
907       do_gdelete();
908     }
909
910 } // end of method do_gdelete_groupId()
911
912 void
913 TPushParser::do_gdelete_phantom_group()
914 {
915   // if we are here, the cursor MUST be a child of a 
916   // phantom group.
917   assert(cursor.parent() && cursor.parent().isG() && !cursor.parent().hasId());
918
919   TNode parent = cursor.parent();
920
921   // now we have to control if the cursor has a preceding element or not
922   TNode prev = cursor.prev();
923   if (prev)
924     {
925       // the cursor has a preceding element, so we try to remove it
926       gdelete_prev();
927
928       if (parent.size() == 1 && parent.parent().isSp())
929         {
930           // in this case the gdelete_prev has removed the only element preceding the cursor.
931           // If the phantom group is an sp's child, it means that the user has removed all \' in the 
932           // phantom group.
933           // We can remove the phamtom group and the sp element. But we also can only remove the 
934           // phantom group, giving to the user the possibility of inserting an exponent.
935           // At the moment, we remove the sp element (and the phantom group), because it may be the correct 
936           // behavior of a graphical deleting.
937           cursor.remove();
938           parent.replace(cursor);
939           // now we have an sp element with two children: the first child (we don't know nothing about it)
940           // and the cursror.
941           assert(cursor.parent().size() == 2);
942           
943           // to delete the script we can invoke the do_gdelete_script(), which will do all controls we need.
944           // To give the possibility of insering an exponent
945           do_gdelete_script();
946         }
947       else if (parent.parent().isSp())
948         {
949           // in this case we have to place the cursor after the sp element
950           cursor.remove();
951           assert(parent.parent().parent());
952           parent.parent().parent().append(cursor);
953         }
954     }
955   else
956     {
957       // in this case the cursor is the only element of the phantom group,
958       // so we have to remove it. But, a phantom group has a special role, 
959       // so we have to control the grand father of the cursor.
960       TNode gfather = parent.parent();
961       if (!gfather)
962         {
963           // If here, the TML tree is in an inconsistent state
964           logger.error("do_gdelete_phantom: TML tree in a inconsistent state");
965         }
966       else if (gfather.isC())
967         {
968           // in this case the phantom group is child of a MACRO.
969           // We have to control the nature of this MACRO.
970           assert(!frames.empty());
971           Frame& frame = frames.top();
972           
973           if (frame.entry.leftOpen && frame.entry.rightOpen)
974             {
975               // in this case, the cursor'parent is in the second and last child 
976               // of the MACRO. We can assert that the grand father has two 
977               // children, which are both phantom group
978               assert(gfather.size() == 2);
979               assert((gfather.last() == parent) && (gfather.first().isG() && !gfather.first().hasId()));
980               assert(frame.pos == 0);
981               
982               TNode ggfather = gfather.parent();
983               assert(ggfather);
984               cursor.remove();
985               parent.remove();
986               // i have to replace the gfather with the elements of its first child
987               gfather.replace(gfather.first().first(), TNode());
988               ggfather.append(cursor);
989               // now we have the situation preceding the insertion of the leftOpen and rightOpen MACRO.
990               // this MACRO no longer exists.
991               frames.pop();
992             }
993           else if (frame.entry.rightOpen)
994             {
995               // the user has inserted a rightOpen MACRO, and now, this MACRO has no children (excluding the 
996               // phantom group), so we remove the MACRO. 
997               // We can assert that cursor's parent is the only child of the MACRO
998               assert(gfather.size() == 1);
999               assert(frame.pos == 0);
1000               cursor.remove();
1001               parent.remove();
1002               gfather.replace(cursor);
1003               
1004               // now we have the situation preceding the rightOpen MACRO, so we have to pop the frame
1005               frames.pop();
1006             }
1007           else if (frame.entry.leftOpen)
1008             {
1009               // this situation will never occur.
1010               logger.error("the parser has generated a wrong TML tree");
1011             }
1012           else if (!frame.entry.pattern.empty())
1013             {
1014               // the MACRO accepts arguments, and the phantom group in which 
1015               // the cursor is, rappresents a delimited argument
1016               // We have to control if the cursor's parent has a preceding element, 
1017               // or not.
1018               TNode uncle = parent.prev();
1019               if (!uncle)
1020                 {
1021                   // the parent is the only element of the MACRO. 
1022                   // we can assert that frame.pos == 0.
1023                   // In this case we can replace the MACRO with the cursor
1024                   assert(frame.pos == 0);
1025                   cursor.remove();
1026                   parent.remove();
1027                   gfather.replace(cursor);
1028                   frames.pop();
1029                 }
1030               else
1031                 {
1032                   // the parent has a preceding element. Now we have 
1033                   // to control if the uncle is a delimited argument or not.
1034                   if (uncle.isG() && !uncle.hasId())
1035                     {
1036                       // the  uncle is a delimited argument. So we have to ideally
1037                       // remove the sequence of delimiters.
1038                       assert(frame.pos > 1);
1039                       unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
1040                       assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
1041                       assert(sequence_length);
1042                       // sequence_length is the length of the delimiters sequence which separates
1043                       // the current parameter and the previous parameter
1044                       frame.pos = frame.pos - sequence_length - 1;
1045                       cursor.remove();
1046                       parent.remove();
1047                       uncle.append(cursor);
1048                     }
1049                   else
1050                     {
1051                       // the uncle is a not delimited argument, so we try to remove it.
1052                       cursor.remove();
1053                       parent.replace(cursor);
1054                       parent = cursor.parent(); // i update the parent (it should be the MACRO)
1055                       assert(parent.isC());
1056
1057                       // now we try to remove the uncle (now it' the preceding element)
1058                       gdelete_prev();
1059                     }
1060                 } // this is the else's end, that handles the case in which an uncle exists
1061             } // end of if (!frame.entry.pattern.empty())
1062           else
1063             {
1064               // the entry has no arguments, is not rightOpen and is not leftOpen.
1065               logger.warning("parser: TML tree in a strange state, but we try to recover from it");
1066               if (gfather.size() == 1)
1067                 {
1068                   cursor.remove();
1069                   parent.remove();
1070                   gfather.replace(cursor);
1071                 }
1072               else
1073                 {
1074                   logger.warning("parser: TML tree in a very strange state, but we try to recover from it");
1075                   cursor.remove();
1076                   gfather.replace(cursor);
1077                 }
1078             }
1079         } // end of if (gfather.isC())
1080       else if (gfather.is("cell"))
1081         {
1082           // A table is a control sequence, so there is a frame in the stack
1083           assert(!frames.empty());
1084           assert(frames.top().pos == 1);
1085           assert(frames.top().entry.table == 1);
1086           
1087           // a cell MUST be a row's child, which in turn is a table's child 
1088           assert(gfather.parent() && gfather.parent().is("row") && gfather.parent().parent());
1089           TNode row = gfather.parent();
1090           
1091           // in this case the cell has no element, so the user wants to delete this cell.
1092           TNode prev_cell = gfather.prev();
1093           cursor.remove();
1094           parent.remove();
1095           gfather.remove();
1096           // now the cell no longer exists
1097
1098           if (!prev_cell)
1099             {
1100               // in this case, the cell is the only cell in the row.
1101               // So, we assume that the user wants to delete the entire row.
1102               TNode table = row.parent();
1103               TNode prev_row = row.prev();
1104               row.remove();
1105               
1106               if (!prev_row)
1107                 {
1108                   // the row was the only child of the table. 
1109                   // so we have to delete the entire table
1110                   assert(table.parent());
1111                   TNode parent_table = table.parent();
1112                   table.remove();
1113                   frames.pop();
1114                   parent_table.append(cursor);
1115                 }
1116               else
1117                 {
1118                   // there are other rows (one or more)
1119                   assert(prev_row.is("row"));
1120                   assert(prev_row.last());
1121                   TNode last_cell = prev_row.last();
1122                   assert(last_cell.is("cell"));
1123                   assert(last_cell.size() == 1);
1124                   assert(last_cell.first().isG() && !last_cell.first().hasId());
1125                   last_cell.first().append(cursor);
1126                 }
1127             } // end of if (!prev_cell)
1128           else
1129             {
1130               // being here means that there is a previous cell,
1131               // so we append the cursor to group.
1132               assert(prev_cell.size() == 1);
1133               assert(prev_cell.first().isG() && !prev_cell.first().hasId());
1134               prev_cell.first().append(cursor);
1135             }
1136         } // end of if (gfather.is("cell"))
1137       else if (gfather.isSp())
1138         {
1139           // in this case, the user typed a \'. So this phantom group 
1140           // contained a sequence of \'. 
1141           // Maybe in this part will never be used, because, if we delete last \' in the 
1142           // phantom group, we remove the phantom group also
1143           //
1144           // In any case, if we are here we have two possibilities: 
1145           //   we can delete the phantom group;
1146           //   we can delete the superscript.
1147           // At the moment we implement the first solution. To implement the second one, just remove 
1148           // delete the line code after the logger.warning and remove comment from the remaining lines
1149           logger.warning("parser: TML tree in a strange state, we try to recover from it");
1150           parent.replace(cursor);
1151           
1152           //cursor.remove();
1153           //parent.remove();
1154           //gfather.replace(cursor);
1155         }
1156       else if (gfather.is("math"))
1157         {
1158           // in this case we ignore the user's will of deleting
1159           // but we can decide to remove the math mode.
1160           logger.info("Parser: nothing to delete");
1161         }
1162       else
1163         {
1164           // cursor' grand father is undefined
1165           logger.error("parser: TML tree is in a unknown state");
1166         }
1167     } // end of the else of the if (prev)
1168
1169 }
1170
1171
1172 void
1173 TPushParser::do_gdelete()
1174 {
1175   // we have to handle the case in wich the cursor has a parent or not
1176   if (!cursor.parent())
1177     {
1178       // it's not a good situation...at the moment we do not take actions
1179       logger.error("TML tree not well structured");
1180     }
1181   else
1182     {
1183       // a parent exists. We have to take differnt actions, based on the nature of 
1184       // the parent
1185       TNode parent = cursor.parent();
1186       if (parent.is("math"))
1187         {
1188           // we ca do two thing...we can remove the math mode (it implies controlling the display attribute), we can do nothing
1189           // At the moment, the user's will of deleting is simply ignored
1190           logger.error("TML tree not well structured");
1191         }
1192       else if (parent.isG())
1193         {
1194           // the cursor's parent is a group. We have to control if it's a phantom group or not
1195           if (parent.hasId())
1196             {
1197               do_gdelete_groupId();
1198             }
1199           else
1200             {
1201               do_gdelete_phantom_group();
1202             }
1203         } // end of parent is group
1204       else if (parent.isC())
1205         {
1206           do_gdelete_macro();
1207         } // end of parent is a MACRO
1208       else if (parent.isSp() || parent.isSb())
1209         {
1210           do_gdelete_script();
1211         } // end of parent is sp or sb
1212     } // end of the else which consider the case in which parent exists
1213   
1214 } // end of method do_gdelete
1215
1216 void
1217 TPushParser::process(const TToken& token)
1218 {
1219   switch (token.category)
1220     {
1221     case TToken::BEGIN: do_begin(); break;
1222     case TToken::END: do_end(); break;
1223     case TToken::SHIFT: do_shift(); break;
1224     case TToken::ALIGN: do_align(); break;
1225     case TToken::EOL: do_eol(); break;
1226     case TToken::PARAMETER: do_parameter(token.value); break;
1227     case TToken::SUPERSCRIPT: do_superscript(); break;
1228     case TToken::SUBSCRIPT: do_subscript(); break;
1229     case TToken::SPACE: do_space(token.value); break;
1230     case TToken::LETTER: do_letter(token.value); break;
1231     case TToken::DIGIT: do_digit(token.value); break;
1232     case TToken::OTHER: do_other(token.value); break;
1233     case TToken::ACTIVE: do_active(token.value); break;
1234     case TToken::COMMENT: do_comment(); break;
1235     case TToken::CONTROL: do_control(token.value); break;
1236     case TToken::GDELETE: do_gdelete(); break;
1237     }
1238 }
1239
1240 void
1241 TPushParser::push(const TToken& token)
1242 {
1243   if (token.category == TToken::GDELETE)
1244     {
1245       process(token);
1246     }
1247   else
1248     {
1249
1250       TNode parent = cursor.parent();
1251       // If the cursor has no parent then it is detached from the editing
1252       // tree, which means this token will be ignored
1253
1254       if (parent)
1255         // If the parent is a phantom group and the grand-parent is a
1256         // control sequence, there are two cases:
1257         // a. we are parsing a delimited argument of a entry
1258         // b. we are parsing a side of a right- or left-open entry
1259         if (parent.isG() && !parent.hasId() && parent.parent().isC())
1260           {
1261             // There must be an open frame, for the grand-parent is a control sequence
1262             assert(!frames.empty());
1263             Frame& frame = frames.top();
1264             if (!frame.entry.pattern.empty())
1265               {
1266                 // The entry pattern is not empty. By our conventions this means
1267                 // the entry cannot be open at either end, hence we are parsing
1268                 // a delimited argument
1269                 assert(frame.pos + 1 < frame.entry.pattern.size());
1270                 assert(frame.entry.pattern[frame.pos + 1].category != TToken::PARAMETER);
1271                 if (frame.entry.pattern[frame.pos + 1] == token)
1272                   {
1273                     // The token matches with a delimiter of the argument, 
1274                     // so we increment the frame.pos
1275                     frame.pos++;
1276
1277                     if (frame.entry.lastDelimiter(frame.pos))
1278                       {
1279                         // this delimiter is the last one for the argumet, 
1280                         // so the argument is completed
1281                         cursor.remove();
1282                         frame.pos++;
1283
1284                         if (frame.pos == frame.entry.pattern.size())
1285                           {
1286                             // This token has completed the entry
1287                             advance(parent);
1288                           }
1289                         else if (frame.entry.paramDelimited(frame.pos))
1290                           {
1291                             // For the next is a delimited argument we have to place
1292                             // a suitable phantom group with the cursor inside
1293                             TNode g = doc.createG();
1294                             parent.parent().append(g);
1295                             g.append(cursor);
1296                           }
1297                         else
1298                           parent.parent().append(cursor);
1299                       }
1300                   }
1301                 else
1302                   {
1303                     // Delimiter mismatch.
1304                     if (frame.entry.pattern[frame.pos].category != TToken::PARAMETER)
1305                       {
1306                         // in this case, there is a sequence of delimiters that delimitates
1307                         // the argument, and the user correctly inserted a portion of this 
1308                         // sequence, but now has inserted a wrong delimiter.
1309                         // Here, there are some possibilities:
1310                         //   - ignore the token, and wait for the correct delimiter
1311                         //   - ignore the token, wait for the correct delimiter and emit an error
1312                         // At the moment, we implement the second possibily
1313                         logger.error("parser: it's not the correct delimiter...you have to type " + frame.entry.pattern[frame.pos + 1].value);
1314                       }
1315                     else
1316                       {
1317                         // in this case, the sequence of delimiters is composed of one 
1318                         // delimiter. It means that we have to process the token
1319                         process(token);
1320                       }
1321                   }
1322               }
1323             else
1324               {
1325                 // The entry pattern is empty, hence we are parsing a right-open
1326                 // entry. What happens if we actually are in the left side?
1327                 // This could happen only when re-editing an entered expression
1328                 // We'll see...
1329                 assert(frame.entry.rightOpen);
1330                 process(token);
1331               }
1332           }
1333         else if (parent.isC())
1334           {
1335             // We are parsing a non-delimited argument entry
1336             // or a fixed token
1337             Frame& frame = frames.top();
1338             assert(frame.pos < frame.entry.pattern.size());
1339
1340             if (frame.entry.pattern[frame.pos].category == TToken::PARAMETER)
1341               {
1342                 // As by the TeX parsing rules of undelimited parameters,
1343                 // empty spaces are ignored
1344                 if (token.category != TToken::SPACE)
1345                   {
1346                     // We need to increase the frame position here, becase inside
1347                     // process the function advance will be called. At that point
1348                     // it will be important for the parser to know that the entry
1349                     // has been completed in order to place the cursor correctly
1350                     // in the next position
1351                     frame.pos++;
1352                     process(token);
1353                   }
1354               }
1355             else if (frame.entry.pattern[frame.pos] == token)
1356               {
1357                 // The token has been accepted
1358                 frame.pos++;
1359                 if (frame.pos < frame.entry.pattern.size() &&
1360                     frame.entry.paramDelimited(frame.pos))
1361                   {
1362                     // If the next is a delimited argument we have to place
1363                     // the phantom group with the cursor inside
1364                     TNode g = doc.createG();
1365                     cursor.replace(g);
1366                     g.append(cursor);
1367                   }
1368                 else
1369                   advance(parent);
1370               }
1371             else
1372               {
1373                 // There is a mismatch. Emit an error and ignore the token?
1374                 logger.debug("parser: token ignored: " + token.value);
1375               }
1376
1377           }
1378         else
1379           process(token);
1380       else
1381         {
1382           logger.warning("ignored token");
1383         }
1384
1385     } // this end corresponds to the else of the if (token.category == TToken::GDELETE)
1386
1387   if (factory) factory->documentModified(doc);
1388
1389 }
1390
1391 void
1392 TPushParser::advance(const TNode& node)
1393 {
1394   assert(node);
1395   TNode parent = node.parent();
1396   if (!parent)
1397     ; // nothing to do, the cursor is not in the document any more
1398   else if (parent.isG())
1399     {
1400       TNode next = node.next();
1401       if (next) next.insert(cursor);
1402       else parent.append(cursor);
1403     }
1404   else if (parent.isC())
1405     {
1406       if (node.next())
1407         ; // cursor removed
1408       else
1409         {
1410           Frame& frame = frames.top();
1411           if (frame.pos == frame.entry.pattern.size())
1412             {
1413               frames.pop();
1414               advance(parent);
1415             }
1416           else
1417             parent.append(cursor);
1418         }
1419     }
1420   else if (parent.is("math"))
1421     ; // we are done
1422   else
1423     advance(parent);
1424 }
1425
1426 void
1427 TPushParser::setCursorHint(const std::string& c)
1428 {
1429   cursor["val"] = c;
1430   if (factory) factory->documentModified(doc);
1431 }