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