]> matita.cs.unibo.it Git - helm.git/blobdiff - helm/DEVEL/mathml_editor/src/TPushParser.cc
ocaml 3.09 transition
[helm.git] / helm / DEVEL / mathml_editor / src / TPushParser.cc
index a063668e0ae31dd03e44f2719733276aa52cda7a..0c96fe43968c94be52a1fdcffac385012d683ebb 100644 (file)
@@ -1,40 +1,86 @@
-
+/* This file is part of EdiTeX, an editor of mathematical
+ * expressions based on TeX syntax.
+ * 
+ * Copyright (C) 2002-2003 Luca Padovani <lpadovan@cs.unibo.it>,
+ *                    2003 Paolo Marinelli <pmarinel@cs.unibo.it>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * For more information, please visit the project's home page
+ * http://helm.cs.unibo.it/editex/
+ * or send an email to <lpadovan@cs.unibo.it>
+ */
+
+#include <cassert>
+
+#include "ALogger.hh"
 #include "TPushParser.hh"
-#include "TListener.hh"
+#include "AMathMLFactory.hh"
 
-TPushParser::TPushParser(const TDictionary& d) : dictionary(d), listener(0)
+TPushParser::TPushParser(ALogger& l, const TDictionary& d) : APushParser(l), dictionary(d)
 {
   init();
 }
 
-TPushParser::TPushParser(const TDictionary& d, TListener& l) : dictionary(d), listener(&l)
+TPushParser::TPushParser(ALogger& l, AMathMLFactory& f, const TDictionary& d) : APushParser(l, f), dictionary(d)
 {
   init();
 }
 
+TPushParser::~TPushParser()
+{
+}
+
 void
 TPushParser::init()
 {
-  nextId = 1;
   cursor = doc.create("cursor");
-  cursor["id"] = "I0";
-  doc.clearDirty();
-  doc.root().append(cursor);
+  cursor["visible"] = "1";
+  hiddenCursor = 0;
+  reset();
 }
 
-TPushParser::~TPushParser()
+void
+TPushParser::reset()
 {
+  nextId = 1;
+  if (cursor.parent()) cursor.remove();
+  cursor["val"] = "";
+  doc.reset();
+  doc.root().append(cursor);
+  if (factory && !frozen()) factory->documentModified(doc);
 }
 
-std::string
-TPushParser::PRIME() const
+TNode
+TPushParser::PRIME()
 {
   const TDictionary::Entry entry = dictionary.find("prime");
-  if (entry.cls == TDictionary::OPERATOR) return entry.value;
-  else return "?";
+  if (entry.cls == TDictionary::OPERATOR)
+    {
+      TNode op = doc.createO(entry.value, nextId++);
+      op["name"] = "prime";
+      return op;
+    }
+  else
+    {
+      TNode op = doc.createO("?", nextId++);
+      return op;
+    }
 }
 
-void
+bool
 TPushParser::do_begin()
 {
   TNode parent = cursor.parent();
@@ -54,9 +100,69 @@ TPushParser::do_begin()
       cursor.replace(g);
       g.append(cursor);
     }
+  return true;
 }
 
-void
+bool
+TPushParser::correctBrace()
+{
+  // this method MUST be invoked when the cursor is child of a 
+  // phantom group, which in turn is the last rightOpen MACRO's child.
+  // The only way to exit from a rightOpen MACRO is opening a group before 
+  // inserting the MACRO and, once the MACRO is completely inserted, closing 
+  // the group.
+  // This method return true if the condition above is true. False, otherwise.
+  assert(cursor.parent() && cursor.parent().isG() && !cursor.parent().hasId());
+  TNode parent = cursor.parent();
+  assert(parent.parent() && parent.parent().isC());
+  assert(!frames.empty());
+  Frame& frame = frames.top();
+  assert(frame.entry.rightOpen);
+  assert(parent.parent().last() == parent);
+  
+  TNode c = parent.parent();
+  bool stop = false;
+  bool ok = false;
+  TNode node = c.parent();
+  do
+    {
+      if (node.isG() && node.hasId())
+        {
+         // in this case, the rightOpen MACRO is a child of a group with id. 
+         // So, the '}' is correct
+         ok = true;
+         stop = true;
+       }
+      else if (node.isG())
+        {
+         // the MACRO is a phantom group's child. We have to control why we 
+         // have this phantom group
+         TNode nodeParent = node.parent();
+         if (nodeParent && nodeParent.isC())
+           {
+             // we have to control the nature of this MACRO
+             const TDictionary::Entry& entry = dictionary.find(nodeParent.nameC());
+             if (entry.rightOpen && node == nodeParent.last())
+               {
+                 // in this case we have to re-iterate the process
+                 node = nodeParent.parent();
+               }
+             else stop = true;
+           }
+         else stop = true;
+       }
+      else
+        {
+         // at the moment we assume that a MACRO cannot be child of an element other than a group
+         stop = true;
+       }
+    }
+  while (!stop);
+
+  return ok;
+}
+
+bool
 TPushParser::do_end()
 {
   TNode parent = cursor.parent();
@@ -65,31 +171,75 @@ TPushParser::do_end()
       // normal closing brace for an explicitly open group
       cursor.remove();
       advance(parent);
+      return true;
     }
   else if (parent && parent.isG() && parent.parent() && parent.parent().is("cell"))
     {
+      assert(!frames.empty());
       // closing brace for a structure in which & or \cr have been used
       TNode row = parent.parent().parent();
       assert(row && row.is("row"));
-      TNode table = row.parent();
-      assert(table);
-      advance(table);
+      assert(row.parent());
+      advance(row);
+      return true;
     }
-  else if (parent && parent.isG() && !parent.hasId() && parent.parent())
+  else if (parent && parent.isG() && !parent.hasId() && parent.parent() && !parent.parent().is("math"))
     {
-      // closing brace for a right-open macro (like \over)
-      cursor.remove();
-      advance(parent.parent());
-      frames.pop();
+      // In this case, we have to control the cursor's grand parent.
+      TNode gparent = parent.parent();
+
+      if (gparent.isC() && gparent.last() == parent)
+        {
+         // a frame MUST be in the stack
+         assert(!frames.empty());
+
+         // we have to control the nature of this macro
+         if (frames.top().entry.rightOpen)
+           {
+             // in this case, the '}' character is the proper way to exit from the phantom group, and 
+             // in particular, this character means that the user wants to exit from the MACRO.
+             // A rightOpen MACRO MUST be descendant of a group with Id. This '}' is the closing brace of this 
+             // group. So, we have to control if this group exists. This groyp could exist, but this MACRO could 
+             // be another MACRO's child, so we have to control this last MACRO recursively. This recurive control 
+             // is done by the correctBrace method.
+             if (!correctBrace())
+               {
+                 // the '}' is not correct
+                 logger.warning("nothing to close");
+                 return false;
+               }
+             else
+               {
+                 cursor.remove();
+                 advance(parent);
+                 return true;
+               }
+           }
+         else
+           {
+             logger.warning("ignored closing brace");
+             return false;
+           }
+       }
+      else
+        {
+         // at the moment, a phantom group with the cursor inside can be a MACRO's child or a cell's child, and these cases
+         // are handled in other blocks of code.
+         logger.error("do_end: strange TML tree");
+         return false;
+       }
     }
   else
     {
-      // ???
-      assert(0);
+      // In this case, there is a redundant '}', so we can ignore it and 
+      // emit an error
+      logger.warning("There is so no corresponding'{'");
+      return false;
+      //assert(0);
     }
 }
 
-void
+bool
 TPushParser::do_shift()
 {
   TNode parent = cursor.parent();
@@ -101,6 +251,7 @@ TPushParser::do_shift()
       cursor.replace(math);
       math.append(g);
       g.append(cursor);
+      return true;
     }
   else if (parent.isG() && !parent.hasId() && parent.parent() && parent.parent().is("math"))
     {
@@ -112,11 +263,14 @@ TPushParser::do_shift()
            {
              // one math shift is enough to close it
              cursor.remove();
+             return true;
            }
          else
            {
              // we need two closing math shifts
+             //cursor.remove(); ??
              parent.parent().append(cursor);
+             return true; // ???
            }
        }
       else if (parent.parent()["display"] != "1")
@@ -124,23 +278,27 @@ TPushParser::do_shift()
          // there is nothing before the cursor, and the math is not
          // in display mode, so this must be a double math shift
          parent.parent()["display"] = "1";
+         return true;
        }
       else
        {
          parent.parent().append(cursor);
+         return true;
        }
     }
   else if (parent.is("math"))
     {
       cursor.remove();
+      return true;
     }
   else
     {
-      cerr << "ERROR: math shift" << endl;
+      logger.warning("not allowed here");
+      return false;
     }
 }
 
-void
+bool
 TPushParser::do_align()
 {
   TNode parent = cursor.parent();
@@ -153,6 +311,7 @@ TPushParser::do_align()
       row.append(cell);
       cell.append(g);
       g.append(parent.first(), cursor);
+      return true;
     }
   else if (parent && parent.isG() && parent.parent().is("cell"))
     {
@@ -167,26 +326,31 @@ TPushParser::do_align()
       TNode g = doc.createG();
       cell.append(g);
       g.append(cursor);
+      return true;
     }
   else
     {
-      cerr << "alignment tab used outside matrix" << endl;
+      logger.warning("alignment tab used outside matrix");
+      return false;
     }
 }
 
-void
+bool
 TPushParser::do_eol()
 {
   //if (cursor.parent()) cursor.remove();
+  logger.warning("ignored token");
+  return false;
 }
 
-void
+bool
 TPushParser::do_parameter(const std::string& p)
 {
-  // ???
+  logger.warning("ignored token");
+  return false;
 }
 
-void
+bool
 TPushParser::do_subscript()
 {
   TNode parent = cursor.parent();
@@ -200,6 +364,7 @@ TPushParser::do_subscript()
          cursor.replace(elem);
          elem.append(g);
          elem.append(cursor);
+         return true;
        }
       else
        {
@@ -207,16 +372,30 @@ TPushParser::do_subscript()
          prev.replace(elem);
          elem.append(prev);
          elem.append(cursor);
+         return true;
        }
     }
   else if (parent.isSb() && cursor == parent[1])
     {
-      if (parent["under"] == "1") cerr << "already under" << endl;
-      else parent["under"] = "1";
+      if (parent["under"] == "1")
+        {
+         logger.warning("already under");
+         return false;
+       }
+      else
+        {
+         parent["under"] = "1";
+         return true;
+       }
+    }
+  else
+    {
+      logger.warning("ignored token");
+      return false;
     }
 }
 
-void
+bool
 TPushParser::do_superscript()
 {
   TNode parent = cursor.parent();
@@ -230,6 +409,7 @@ TPushParser::do_superscript()
          cursor.replace(elem);
          elem.append(g);
          elem.append(cursor);
+         return true;
        }
       else
        {
@@ -237,56 +417,71 @@ TPushParser::do_superscript()
          prev.replace(elem);
          elem.append(prev);
          elem.append(cursor);
+         return true;
        }
     }
   else if (parent.isSp() && cursor == parent[1])
     {
-      if (parent["over"] == "1") cerr << "already over" << endl;
-      else parent["over"] = "1";
+      if (parent["over"] == "1")
+        {
+         logger.warning("already over");
+         return false;
+       }
+      else
+        {
+         parent["over"] = "1";
+         return true;
+       }
+    }
+  else
+    {
+      logger.warning("ignored token");
+      return false;
     }
 }
 
-void
+bool
+TPushParser::do_ignorablespace(const std::string& s)
+{
+  // At the moment, do nothing
+}
+
+bool
 TPushParser::do_space(const std::string&)
 {
-  // ? may be used to distinguish tokens in some mode?
+  TNode elem = doc.createS(nextId++);
+  cursor.replace(elem);
+  advance(elem);
+  return true;
 }
 
-void
+bool
 TPushParser::do_letter(const std::string& s)
 {
   //TNode parent = cursor.parent();
   TNode elem = doc.createI(s, nextId++);
   cursor.replace(elem);
   advance(elem);
+  return true;
 }
 
-void
+bool
 TPushParser::do_digit(const std::string& s)
 {
-  TNode parent = cursor.parent();
-  TNode prev = cursor.prev();
-  if (prev && parent.isG() && prev.is("n"))
-    {
-      TNode elem = doc.createN(prev.value() + s, nextId++);
-      prev.replace(elem);
-    }
-  else
-    {
-      TNode elem = doc.createN(s, nextId++);
-      cursor.replace(elem);
-      advance(elem);
-    }
+  TNode elem = doc.createN(s, nextId++);
+  cursor.replace(elem);
+  advance(elem);
+  return true;
 }
 
 bool
 TPushParser::isPrimes(const TNode& node) const
 {
   assert(node);
-  return node.isG() && node.last() && node.last().is("o") && node.last()["val"] == PRIME();
+  return node.isG() && node.last() && node.last().is("o") && node.last()["name"] == "prime";
 }
 
-void
+bool
 TPushParser::do_apostrophe()
 {
   if (cursor.parent() && cursor.parent().isG())
@@ -294,11 +489,17 @@ TPushParser::do_apostrophe()
       if (TNode prev = cursor.prev())
        {
          if (prev.isSp() && prev[1] && isPrimes(prev[1]))
-           prev[1].append(doc.createO(PRIME(), nextId++));
+           {
+             prev[1].append(PRIME());
+             return true;
+           }
          else if (prev.isSb() && prev[0] &&
                   prev[0].isSp() && prev[0][1] &&
                   isPrimes(prev[0][1]))
-           prev[0][1].append(doc.createO(PRIME(), nextId++));
+           {
+             prev[0][1].append(PRIME());
+             return true;
+           }
          else
            {
              TNode elem = doc.create("sp");
@@ -306,51 +507,59 @@ TPushParser::do_apostrophe()
              prev.replace(elem);
              elem.append(prev);
              elem.append(g);
-             g.append(doc.createO(PRIME(), nextId++));
+             g.append(PRIME());
+             return true;
            }
        }
       else
        {
-         // error ???
+         // is it an error?
+         logger.warning("you have to insert an identifier before a  ''");
+         return false;
        }
     }
   else
     {
-      // error ??
+      logger.warning("cursor has to be in a group");
+      return false;
     }
 }
 
-void
+bool
 TPushParser::do_other(const std::string& s)
 {
   switch (s[0])
     {
     case '\'':
-      do_apostrophe();
+      return do_apostrophe();
       break;
     default:
-      cout << "TPushParser::do_other " << s << endl;
-      cout << "DOCUMENT: " << static_cast<GdomeNode*>(cursor.element().get_ownerDocument()) << endl;
+      /*cout << "TPushParser::do_other " << s << endl;
+      cout << "DOCUMENT: " << static_cast<GdomeNode*>(cursor.element().get_ownerDocument()) << endl;*/
       TNode elem = doc.createT("o", s, nextId++);
       cursor.replace(elem);
-      advance(elem);  
+      advance(elem);
+      return true;
       break;
     }
 }
 
-void
+bool
 TPushParser::do_active(const std::string&)
 {
   // ??? space?
+  logger.warning("ignored token");
+  return false;
 }
 
-void
+bool
 TPushParser::do_comment()
 {
   // ???
+  return false;
 }
 
-void
+bool
 TPushParser::do_cr()
 {
   TNode parent = cursor.parent();
@@ -370,13 +579,20 @@ TPushParser::do_cr()
       row.append(cell);
       cell.append(g);
       g.append(cursor);
+      return true;
+    }
+  else
+    {
+      // at the moment, \cr can only be used inside a table
+      logger.warning("cr used outside a table");
+      return false;
     }
 }
 
-void
+bool
 TPushParser::do_control(const std::string& name)
 {
-  if (name == "cr") do_cr();
+  if (name == "cr") return do_cr();
   else
     {
       TNode parent = cursor.parent();
@@ -389,6 +605,7 @@ TPushParser::do_control(const std::string& name)
            t["name"] = name;
            cursor.replace(t);
            advance(t);
+           return true;
          }
          break;
        case TDictionary::OPERATOR:
@@ -397,6 +614,7 @@ TPushParser::do_control(const std::string& name)
            t["name"] = name;
            cursor.replace(t);
            advance(t);
+           return true;
          }
          break;
        case TDictionary::NUMBER:
@@ -405,54 +623,49 @@ TPushParser::do_control(const std::string& name)
            t["name"] = name;
            cursor.replace(t);
            advance(t);
+           return true;
          }
          break;
        case TDictionary::MACRO:
          {
-           TNode m = doc.createC(name, nextId++);
-           cursor.replace(m);
-           
-           cout << "ecco tutti i token del pattern della entry inserita" << endl;
-           for (unsigned i = 0; i < entry.pattern.size(); i++)
-             cout << entry.pattern[i].value << endl;
-           
-           if (entry.leftOpen && entry.rightOpen)
-             {
-               assert(entry.pattern.empty());
-               assert(parent.isG());
-               TNode g1 = doc.createG();
-               g1["left-open"] = "1";
-               g1.append(parent.first(), m);
-               m.append(g1);
-               TNode g2 = doc.createG();
-               g2.append(cursor);
-               m.append(g2);
-               frames.push(Frame(entry));
-             }
-           else if (entry.leftOpen)
-             {
-               assert(parent.isG());
-               TNode g = doc.createG();
-               g["left-open"] = "1";
-               g.append(parent.first(), m);
-               m.append(g);
-               advance(m);
-             }
-           else if (entry.rightOpen)
+           if (parent.isG())
              {
-               assert(entry.pattern.empty());
-               assert(parent.isG());
-               TNode g = doc.createG();
-               g.append(cursor);
-               m.append(g);
-               frames.push(Frame(entry));
-             }
-           else if (!entry.pattern.empty())
-             {
-               if (parent.isG())
-                 {
+               TNode m = doc.createC(name, nextId++);
+               cursor.replace(m);
+               if (entry.leftOpen && entry.rightOpen)
+                 {
+                   assert(entry.pattern.empty());
+                   assert(parent.isG());
+                   TNode g1 = doc.createG();
+                   g1["left-open"] = "1";
+                   g1.append(parent.first(), m);
+                   m.append(g1);
+                   TNode g2 = doc.createG();
+                   g2.append(cursor);
+                   m.append(g2);
+                   frames.push(Frame(entry));
+                 }
+               else if (entry.leftOpen)
+                 {
+                   assert(parent.isG());
+                   TNode g = doc.createG();
+                   g["left-open"] = "1";
+                   g.append(parent.first(), m);
+                   m.append(g);
+                   advance(m);
+                 }
+               else if (entry.rightOpen)
+                 {
+                   assert(entry.pattern.empty());
+                   assert(parent.isG());
+                   TNode g = doc.createG();
+                   g.append(cursor);
+                   m.append(g);
+                   frames.push(Frame(entry));
+                 }
+               else if (!entry.pattern.empty())
+                 {
                    frames.push(Frame(entry));
-                   cout << "do_control: valore di pos del frame inserito " << frames.top().pos << endl;
                    if (entry.paramDelimited(0))
                      {
                        TNode g = doc.createG();
@@ -464,160 +677,403 @@ TPushParser::do_control(const std::string& name)
                  }
                else
                  {
-                   // error, but we could handle this very easily
-                   cerr << "error, but we could handle this easily" << endl;
-                 }
+                   // it's an empty macro
+                   advance(m);
+                 }
+               return true;
+             }
+           else if (!entry.pattern.size() && !entry.rightOpen && !entry.leftOpen)
+             {
+               // a macro with no arguments and no right open and no left open, can be child of anything
+               TNode m = doc.createC(name, nextId++);
+               cursor.replace(m);
+               advance(m);
+               return true;
+             }
+           else
+             {
+               // a macro with arguments or a rightOpen or leftOpen macro must be a group's child
+               logger.warning("ignored token: this macro should be in a group");
+               return false;
              }
-           else advance(m);
          }
          break;
        case TDictionary::UNDEFINED:
          {
-           cerr << "ERROR: using undefined macro `" << name << "'" << endl;
+           logger.warning("using undefined macro " + name);
            TNode m = doc.createC(name, nextId++);
            cursor.replace(m);
            advance(m);
+           return true;
          }
          break;
        default:
-         assert(0);
+         {
+           //assert(0);
+           logger.warning("ignored token");
+           return false;
+         }
        }
     }
 }
 
-void
-TPushParser::gdelete_prev()
+std::string
+TPushParser::drop_prev_token(bool special)
 {
-  // if in this function, the prev of cursor does exist, also the parent and we want a graphical deleting.
-
   assert(cursor.prev());
   assert(cursor.parent());
-
   TNode prev = cursor.prev();
-  if (prev.is("i") || prev.is("o") || prev.is("n"))
+  assert(prev.isT());
+  DOM::UCS4String ucs4val = prev.element().getAttribute("val");
+  bool macro = prev.element().hasAttribute("name");
+  std::string utf8name;
+  if (macro) utf8name = prev.element().getAttribute("name");
+  
+  cursor.remove();
+  prev.replace(cursor);
+  
+  if (cursor.parent().isC())
     {
-      // the control below is designed to handle the case in which val have more than one unicode character
-      DOM::UCS4String ucs4val(prev.element().getAttribute("val"));
-      if ((ucs4val.length() <= 1) || prev.element().hasAttribute("name"))
-        {
-         cursor.remove();
-         prev.replace(cursor);
+      // in this case we have removed an element of a MACRO. 
+      // we can assert that this element was a non delimited argument
+      assert(!frames.empty());
+      Frame& frame = frames.top();
+      assert(frame.pos > 0);
+      frame.pos--;
+    }
 
-         if (cursor.parent().isC())
+  if ((ucs4val.length() > 1))
+    {
+      if (!macro)
+        {
+         // in this case we can return the content of ucs4val, but we have 
+         // to convert it in a utf8
+         DOM::GdomeString gdsval(ucs4val);
+         std::string utf8val(gdsval);
+         switch (utf8val[utf8val.length() - 1])
            {
-             // in this case we have removed an element of a MACRO. 
-             // we can assert that this element was a non delimited argument
-             assert(!frames.empty());
-             Frame& frame = frames.top();
-             assert(frame.pos > 0);
-
-             frame.pos--;
+           case '-':
+           case '_':
+             return (special) ? std::string(utf8val, 0, utf8val.length() - 1) + "\\" : std::string(utf8val, 0, utf8val.length() - 1);
+           default: return (special) ? "" : std::string(utf8val, 0, utf8val.length() - 1);
            }
        }
       else
         {
-         ucs4val.erase(ucs4val.length() - 1, 1);
-         prev.element().setAttribute(DOM::GdomeString("val"), DOM::GdomeString(ucs4val));
+         // in this case, the content of val could be in unicode, 
+         // but we have the attribute name, which doesn't contain character not representable 
+         // with a byte.
+         return (special) ? "\\" + utf8name : "";
        }
-    } // end of if (prev.is("i") || ...)
-  else if (prev.is("sp") || prev.is("sb"))
+    }
+  else if (macro && special) return "\\" + utf8name;
+  else return "";
+}
+
+std::string
+TPushParser::drop_prev_script(bool special)
+{
+  // this method deletes an sp or an sb preceding the cursor
+  assert(cursor.prev());
+  assert(cursor.parent());
+  TNode prev = cursor.prev();
+  assert(prev.is("sp") || prev.is("sb"));
+  cursor.remove();
+  prev.append(cursor);
+  // we can invoke the drop_prev, because a sp (sb) MUST have two children
+  // but we cannot invoke do_drop_script because it assumes when called, the first 
+  // child has been removed yet.
+  if (cursor.prev().isG() && !prev.hasId())
     {
-      cursor.remove();
-      prev.append(cursor);
-      gdelete_prev();
-    } // end of if (prev.is("sp") || prev.is("sb"))
-  else if (prev.isG())
+      // in this case, the user has inserted a sequence of '.
+      // Hence, we force a normal deletion, because the behavior must be the same 
+      // for the two kind of deletion
+      return drop_prev(false);
+    }
+  else return drop_prev(special);
+}
+
+std::string
+TPushParser::drop_prev_group(bool special)
+{
+  assert(cursor.prev() && cursor.prev().isG());
+  TNode parent = cursor.parent();
+  TNode prev = cursor.prev();
+  cursor.remove();
+  prev.append(cursor);
+
+  if (parent.isC() && prev.hasId())
     {
-      cursor.remove();
-      prev.append(cursor);
-      do_gdelete();
+      // this previous group is a macro's argument. Entering inside it means that
+      // this argument becomes incomplete. Hence, we have to decrement the member pos.
+      assert(!frames.empty());
+      frames.top().pos--;
     }
-  else if (prev.isC())
+
+  if (special) return "";
+  else
     {
-      const TDictionary::Entry& entry = dictionary.find(prev["name"]);
+      // a group could have no children, so the drop_prev is not appropriate
+      // so, this method is not equivalent to the one above
+      return do_drop(special);
+    }
+}
 
-      // i start to remove a MACRO. The frame associated to this MACRO was 
-      // popped from the stack (i think). So, i re-push the frame in the stack,
-      // but the pos member should be set correctly
-      cout << "gdelete_prev: i have to start to delete a MACRO" << endl;
+std::string
+TPushParser::drop_prev_macro(bool special)
+{
+  assert(cursor.parent());
+  assert(cursor.prev());
+  TNode prev = cursor.prev();
+  assert(prev.isC());
+
+  std::string macro_name = prev.nameC();
+
+  TNode parent = cursor.parent();
+  
+  const TDictionary::Entry& entry = dictionary.find(prev["name"]);
+  
+  if (!entry.defined())
+    {
+      // In this case, with a normal deletion, we completely remove the macro.
+      // With a special deletion, we remove the last character of the macro's name.
+      cursor.remove();
+      prev.replace(cursor);
+      if (cursor.parent().isC())
+        {
+         // we have removed a macro's child
+         assert(!frames.empty());
+         frames.top().pos--;
+       }
+      if (special) return "\\" + macro_name.erase(macro_name.length() - 1, 1); // we remove the last char, because an undefined macro's name is visible
+      return "";
+    }
+  else
+    {   
+      // we start to remove a MACRO. Different actions must be taken, based on the nature 
+      // of the MACRO. In some cases, we can't remove the MACRO immediately, in other
+      // cases it's correct. In the first set of cases, we have to update the stack, pushing
+      // a frame in it with a correct value of pos, in the 
+      // second one, we must not push a frame in the stack
       
-      // if the control element is leftOpen and rightOpen, the cursor should be placed after 
-      // the last child of the control element's last child
       if (entry.rightOpen)
         {
-         cout << "gdelte_prev(): i have to delete a control rightOpen, so i push an element in the stack" << endl;
-         Frame frame(entry);
-         frames.push(frame);
+         // In this fragment of code we also handle the leftOpen && rightOpen MACRO.
+         // since the control element is rightOpen, the cursor should be placed after 
+         // the last child of the control element's last child, and than, we try to remove something.
+         // A frame MUST be pushed in the stack, because we dont' know if the following actions 
+         // will completely remove the MACRO.
+         frames.push(Frame(entry));
+
+         // Since the MACRO is rightOpen, the last child of the MACRO must be a phantom group
+         assert(prev.last().isG() && !prev.last().hasId());
          
          cursor.remove();
          prev.last().append(cursor);
-         do_gdelete();
+
+         if (special) return "";
+         else
+           {
+             // the drop_prev is not appropriate, because the last child of the MACRO could have no children
+             return do_drop_phantom_group(special);
+           }
        }
       else if (entry.leftOpen)
         {
-         cout << "gdelete_prev: i have to delete a control element with leftOpen" << endl;
+         // the leftOpen MACRO MUST have one and only one child, which MUST be a phantom group
+         // In this case, we do not have to push a frame in the stack, because we remove the 
+         // MACRO immediately, substituting it with the content of the phantom group.
+         // We could remove the last child of the phantom group, but
+         // it's not clear if it's the correct behavior of the graphical deletion.
+         // At the moment, to give a standard behavior, we remove the last element.
+         // With a special deletion, we do not remove it.
          assert(prev.first());
          assert(prev.first().isG());
          assert(prev.first() == prev.last());
+         
          TNode g = prev.first();
-         g.remove();
-         prev.replace(g.first(), TNode());
+         if (g.size())
+           {
+             // in this case, the phantom group has at least one child, so we can call the 
+             // TNode::replace.
+             g.remove();
+             prev.replace(g.first(), TNode());
+             parent.append(cursor);
+             if (special) return "\\" + macro_name;
+             else return do_drop(special);
+           }
+         else
+           {
+             // otherwise, the phantom group has no children, so we remove it, also the MACRO.
+             cursor.remove();
+             g.remove();
+             prev.replace(cursor);
+             if (special) return "\\" + macro_name;
+             else
+               {
+                 // Once removed this empty macro, we could try to remove something else.
+                 // This would be justified by the fact that, generally, an empty macro gives no visual information 
+                 // about it.
+                 return do_drop(special); // special is false
+               }
+           }
        }
       else if (!entry.pattern.empty())
-        { 
-         // we have to start removing a MACRO which accepts arguments.
-         // a MACRO without child does not exist
-         
-         cout << "gdelete_prev: i have to remove a MACRO with argument" << endl;
-
+        {
+         // we have to start to remove a MACRO which accepts arguments.
+         // If the MACRO accepts arguments, the MACRO has at least one child     
          assert(prev.size() >= 1);
 
+         // Differnt actions must be taken, based on the nature of the last child
+         // of the MACRO. We have to distinguish the case in which it's a delimited argument,
+         // frome the one in which it's a not delimited argument.
          if (prev.last().isG() && !prev.last().hasId())
            {
-             // this means that the last child of the MACRO is a phantom group,
-             // which in turn means that it is a delimited argument
-             // so we have to ideally remove this delimiter
-             cursor.remove();
-             prev.last().append(cursor);
-             // i have to push a frame with a correct value of pos
-             Frame frame(entry);
-             frame.pos = entry.pattern.size() - 2;
-             frames.push(frame);
+             if (special)
+               {
+                 // in this case, we have to start removing the last delimiter
+                 frames.push(Frame(entry, entry.pattern.size() - 2));
+                 
+                 cursor.remove();
+                 prev.last().append(cursor);
+
+                 std::string last_del = entry.pattern[entry.pattern.size() - 1].value;
+
+                 return "\\" + last_del;
+               }
+             else
+               {
+                 // the last argument of the MACRO is a delimited argumet. We ideally remove 
+                 // the sequence of delimiters
+                 cursor.remove();
+                 prev.last().append(cursor);
+                 //  we have to push a frame with a correct value of pos
+                 assert(entry.previousParam(entry.pattern.size()) != entry.pattern.size());
+                 
+                 unsigned sequence_length = entry.pattern.size() - entry.previousParam(entry.pattern.size()) - 1;
+                 unsigned p = entry.pattern.size() - sequence_length - 1;
+                 // now, p is the correct value of pos, and we can push the frame.
+                 frames.push(Frame(entry, p));
+                 
+                 // To give a standard behavior to the graphical deletion, we remove the last 
+                 // element of the macro. Since we are in a phantom group, we can invoke the 
+                 // do_drop_phantom_group(special).
+                 return do_drop_phantom_group(special);
+               }
            }
          else
            {
-             // in this case, the last child of the MACRO is 
-             // an argument which is NOT delimited, so i try to 
-             // remove it
-             cout << "gdelete_prev: i try to remove the last argumet of the MACRO" << endl;
+             // in this case, the last child of the MACRO is not a delimited argument, so we try 
+             // to remove it, but we have to take differnt actions if the MACRO is a table with rows or not. 
              cursor.remove();
-             prev.append(cursor); // now prev is the cursor's parent
-
-             Frame frame(entry);
-             frame.pos = entry.pattern.size();
-             frames.push(frame);
-             
-             gdelete_prev();
-
-           }
+             if (entry.table == 1 && prev.last().is("row"))
+               {
+                 // in this case the cursor has to be appended to the group associated to 
+                 // the last cell of the last row of the table
+                 assert(prev.last().last().is("cell") && prev.last().last().first().isG());
+                 prev.last().last().first().append(cursor);
+                 
+                 // we have to push a frame in the stack. Since tables has a pattern size = 1, we have to 
+                 // set pos at 0, because appending the cursor to the last cell means that this argument 
+                 // is not whole inserted.
+                 // We don't call frames.push(Frame(entry)), because it incoditionaly set pos at 0. The 
+                 // following line is more general
+                 frames.push(Frame(entry, entry.pattern.size() - 1));
+                 if (special)
+                   {
+                     // to type a table with rows and cells, the user had typed a 
+                     // "{", and to exit from it, the user had inserted a "}".
+                     // Since we are in a special deletion, we just idealy remove the "}"
+                     return "";
+                   }
+                 else return do_drop_phantom_group(special);
+               }
+             else
+               {
+                 // we push a frame in the stack with a correct value of member pos.
+                 // This correct value is the size of the pattern - 1, because we have been started to delete 
+                 // a MACRO. It means that all of the MACRO's arguments have been inserted, but 
+                 frames.push(Frame(entry, entry.pattern.size()));
+                 prev.append(cursor);
+                 return drop_prev(special);
+               }
+
+           } // end of the else of the if (prev.last().isG() && !prev.last().hasId())
+
+       } // end of if (!entry.pattern.empty())
+      else
+        {
+         // if we are here, the MACRO preceding the cursor, is !(rightOpen || leftOpen),
+         // and has no pattern. It means that it has no children.
+         // We can replace it with the cursor
+         assert(prev.size() == 0);
+         cursor.remove();
+         prev.replace(cursor);
+         if (cursor.parent().isC())
+           {
+             // we have removed an empty macro, which was a non delimited argument of a macro.
+             // We have to decrement pos
+             assert(!frames.empty());
+             frames.top().pos--;
+           }
+
+         if (special) return "\\" + macro_name;
+         else return "";
+                 
+         // now we could start to remove something else. This behavior would be justified by the 
+         // fact that, generally, an empty MACRO gives no visual information about it.
+         // To adopt this behavior, just remove the comment to the following instruction
+         // return do_drop(special);
        }
+    } // end of defined MACRO
+
+}
+
+std::string
+TPushParser::drop_prev(bool special)
+{
+  // if in this function, the prev of cursor does exist, also the parent and we want a graphical deletion.
+  
+  assert(cursor.prev());
+  assert(cursor.parent());
+
+  TNode prev = cursor.prev();
+
+  if (prev.isT())
+    {
+      return drop_prev_token(special);
+    }
+  else if (prev.isSp() || prev.isSb())
+    {
+      return drop_prev_script(special);
+    }
+  else if (prev.isG())
+    {
+      return drop_prev_group(special);
+    }
+  else if (prev.isC())
+    {
+      // here, we also treat the case in which the MACRO is a table
+      return drop_prev_macro(special);
     }
   else 
     {
-      // not handled
+      // not handled. Future cases...
+      return "";
     }
-  
+
 } // end of method
 
 void
-TPushParser::rgreplace_futher(void)
+TPushParser::rgreplace_father()
 {
-  // this function MUST only be invoked, when the cursor
+  // this method MUST only be invoked, when the cursor
   // is the only child of a group with id. This function 
-  // replace the group with the cursor. But if the new parent
+  // replaces the group with the cursor. But if the new parent
   // is a group with id and the cursor is the only child of the 
   // group, the new parent is replaced...and so on.
-  // r stands for recursive, g stands for graphical
+  // r stands for recursive, g stands for graphical.
   assert(cursor.parent());
   assert(cursor.parent().isG() && cursor.parent().hasId());
 
@@ -628,322 +1084,649 @@ TPushParser::rgreplace_futher(void)
       parent.replace(cursor);
       parent = cursor.parent();
     }
-
-  if (parent.isC())
-    {
-      // in this case we have removed a MACRO's child. 
-      // I can assert that this MACRO accepts arguments.
-      assert(!frames.empty());
-      Frame& frame = frames.top();
-      assert(frame.pos > 0);
-      frame.pos--;
-    }
 }
 
-void
-TPushParser::do_gdelete()
+std::string
+TPushParser::do_drop_script(bool special)
 {
-  // this function operates a graphical deleting
+  // If we are here, the cursor is child of a script (sp or sb) and 
+  // this means that a prev does exist and that there is one and only one 
+  // element preceding the cursor. The sp's (or sb's) parent 
+  // MUST NOT be a MACRO.
+  // The element preceding the cursor is the base of the script.
+
+  assert(cursor.parent() && (cursor.parent().isSp() || cursor.parent().isSb()));
+  TNode parent = cursor.parent();
+         
+  assert(parent.size() == 2);
+  assert(parent.parent() && !parent.parent().isC());
   
-  //if (!frames.empty())
-  //  cout << "do_gdelete: c'e' un frame aperto e il suo pos vale: " << frames.top().pos << endl;
+  TNode prev = cursor.prev();
+  cursor.remove();
+  if (prev.isG() /*&& !prev.hasId()*/ && (prev.size() == 0))
+    {
+      // in this case, the script's base is a group with no elements, so 
+      // we have to remove the entire MACRO, replacing it with the cursor.
+      // This situation occurs when the user had typed something like this
+      //   $....{}^
+      // or this 
+      //   $^
+      // or this
+      //   $...{^
+      //
+      if (special && prev.hasId())
+        {
+         // in this case, the user has typed: ...{}^
+         // hence we idealy remove the ^
+         parent.replace(prev);
+         prev.parent().append(cursor);
+         return "";
+       }
+      else if (!prev.hasId())
+        {
+         // we idealy remove the ^, but the phantom group 
+         // has to be removed, also
+         prev.remove();
+         parent.replace(cursor);
+         return "";
+       }
+      else
+        {
+         prev.remove();
+         parent.replace(cursor);
+         
+         // since the script had no children, we can try to remove something else.
+         // Since we don't know who is cursor's parent, and who is cursor's preceding 
+         // element, we invoke the do_drop()
+         return do_drop(special);
+       }
+    }
+  else
+    {
+      // in this case, the prev has to replace the script.
+      parent.replace(prev);
+      prev.parent().append(cursor);
+      // now prev have a preceding element
+      assert(cursor.parent().size() > 1);
+
+      if (special) return "";
+      else
+        {
+         // to give a standard behavior, we try to remove the element, which was 
+         // the script's base.
+         return do_drop(special);
+       }
+    }
   
+} // end of method do_drop_script
+
+std::string
+TPushParser::do_drop_macro(bool special)
+{
+  // If we are here, the cursor is a child of a MACRO and this means
+  // that there is an open frame for the control element
+  // and this element is closed at either side (no leftOpen no rightOpen)
+  // and the MACRO is waiting for a not delimited argument, so 
+  // we can assert that frame.entry.pattern.size() >= 1
+  assert(cursor.parent() && cursor.parent().isC());
   TNode parent = cursor.parent();
 
-  // if no parent, do nothing
-  if (parent)
+  // this string is useful iff we have a special deletion.
+  std::string macro_name = parent.nameC();
+  
+  assert(!frames.empty());
+  Frame& frame = frames.top();
+  assert(frame.entry.pattern.size() >= 1);
+
+  // we have to take different actions, based on if a preceding element exists 
+  // or not
+  TNode prev = cursor.prev();
+  if (!prev)
     {
-      assert(parent);
-      if (parent.isG())
-        {
-         TNode prev = cursor.prev();
-         if (prev)
-           {
-             // i try to delete the preceding element
-             gdelete_prev();
+      // in this case, a prev does not exist, so the actions of deleting means 
+      // that we have to remove the MACRO. So we have to pop the stack.
+      assert(frame.pos == 0);
+      
+      parent.replace(cursor);
+      frames.pop();
 
-             if ((parent.first() == cursor) && parent.isG() && parent.hasId())
-               rgreplace_futher();
+      if (special) return "\\" + macro_name;
+      else
+        {
+         // Since the macro had no children and this is a graphical deletion, we try 
+         // to remove something else
+         return do_drop(special);
+       }
+    }
+  else
+    {
+      // a prev does exist, we have to control if it's a delimited argument or not.
+      if (prev.isG() && !prev.hasId())
+        {
+         // in this case, prev is a delimited argument, so we have 
+         // to ideally remove the sequence of delimiters
+         Frame& frame = frames.top();
+         assert(frame.pos > 1);
+         cursor.remove();
+         prev.append(cursor);
+         assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
 
+         if (special)
+           {
+             // in this case we have to start removing the last delimimeter.
+             // It means that we return in a situation where the user has not entirely 
+             // inserted the delimited argument. So, we have to decrement frame.pos of 
+             // two units: the delimiter and the actual argument
+             std::string last_del = frame.entry.pattern[frame.pos - 1].value;
+             frame.pos = frame.pos - 2;
+             return "\\" + last_del;
            }
-         else // no previous node is present
+         else
            {
-             // if here, we are in a gruop whose only child is the cursor.
+             // these 3 lines of code update the member pos.
+             unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
+             assert(sequence_length);
+             frame.pos = frame.pos - sequence_length - 1;
              
-             if (!parent.hasId())
-               {
-                 // the parent is a phantom group
-                 assert(parent.parent());
-                 if (!parent.parent().is("math"))
-                   {
-                     TNode gfuther = parent.parent();
-
-                     // if the grand futher is a group with Id, it should be removed, 
-                     // but i don't know if it will never occur...
-                     if (gfuther.isG() && gfuther.hasId())
-                       {
-                         cursor.remove();
-                         parent.replace(cursor);
+             // since it's a graphical deletion, we have to remove the current preceding element.
+             // We don't invoke the drop_prev(), because a do_drop_phantom_group is more general.
+             return do_drop_phantom_group(special);
+           }
+       }
+      else
+        {
+         // the prev is not a delimited argument, so we have to try to remove it. 
+         // We "try", because the prev might be something that 
+         // a simple deletion cannot remove completely
+         return drop_prev(special);
+       }
+    }
 
-                         // re-start the process
-                         do_gdelete();
-                       }
-                     else if (gfuther.isC())
-                       {
-                         // the grand futher is a control element: since the parent is a phantom group, 
-                         // the TML tree should be in a inconsistent state (once removed the parent).
-
-                         // being here means that there is a frame in the stack
-                         assert(!frames.empty());
-                         cout << "do_gdelete: i have to remove a phantom group whuch is a child of a MACRO" << endl;
-                         Frame& frame = frames.top();
-                         if (frame.entry.leftOpen && frame.entry.rightOpen)
-                           {
-                             // in this case, the cursor is in the second and last child 
-                             // of the MACRO. We can assert that the grand futher has two 
-                             // children. which are both phantom group
-                             cout << "do_gdelete: the MACRO is leftOpen and rigthOpen" << endl;
-                             assert(gfuther.size() == 2);
-                             assert((gfuther.last() == parent) && (gfuther.first().isG() && !gfuther.first().hasId()));
-                             assert(frame.pos == 0);
-                           
-                             TNode ggfuther = gfuther.parent();
-                             assert(ggfuther);
-                             cursor.remove();
-                             parent.remove();
-                             // i have to replace the gfuther with the elements of its first child
-                             gfuther.replace(gfuther.first().first(), TNode());
-                             cout << "do_gdelete: i have removed the control element, and replaced it with its first child" << endl;
-                             ggfuther.append(cursor);
-                             cout << "do_gdelete: cursor appended to the grand grand futher" << endl;
-
-                             // now we have the situation preceding the insertion of the MACRO leftOpen and rightOpen
-                             // this MACRO no longer exists.
-                             frames.pop();
-                           }
-                         else if (frame.entry.rightOpen)
-                           {
-                             // the user has inserted a MACRO rightOpen. Since the cursor is the 
-                             // only child of the MACRO, the user want to remove it. 
-                             // We can assert that cursor's parent is the only child of the MACRO
-                             cout << "do_gdelete: the MACRO is rightOpen only" << endl;
-                             assert(gfuther.size() == 1);
-                             assert(frame.pos == 0); // i think this assert has no sense
-                           
-                             cursor.remove();
-                             parent.remove();
-                             gfuther.replace(cursor);
-
-                             // now we have the situation preceding the MACRO rightOpen, so i have to pop the frame
-                             frames.pop();
-                           }
-                         else if (frame.entry.leftOpen)
-                           {
-                             // it' s an unpredicted situation
-                             cout << "it's a bad situation, maybe handlable, but unpredicted" << endl;
-                           }
-                         else if (!frame.entry.pattern.empty())
-                           {
-                             // the MACRO (the cursor's grand futher) accepts arguments.
-                             // we have to control if the cursor's uncle does exist.
-
-                             if (parent.prev())
-                               {
-                                 // in this case, we can assert that frame in the stack has 
-                                 // pos greater than 0
-                                 assert(frame.pos > 0);
-                               
-                                 // cursor's uncle does exist. we have to control 
-                                 // its nature (is it a phantom group?)
-                                 TNode uncle = parent.prev();
-                                 if (uncle.isG() && !uncle.hasId())
-                                   {
-                                     // the cursor's parent is a phantom group, so it was a 
-                                     // delimited argument of the MACRO and the corrisponding 
-                                     // delimeter is inserted. So, the action of deleting means
-                                     // removing this delimeter
-                                     cursor.remove();
-                                     parent.remove();
-                                     uncle.append(cursor);
-                                     frame.pos -= 2;
-                                   }
-                                 else
-                                   {
-                                     // the uncle was a NOT delimited argument. So i try to 
-                                     // remove it
-                                     cursor.remove();
-                                     parent.replace(cursor);
-                                 
-                                     parent = cursor.parent(); // i update the parent (it should be the MACRO)
-                                     assert(parent.isC());
-                                 
-                                     gdelete_prev();
-
-                                   }
-                               }
-                             else
-                               {
-                                 // cursor's parent is the only child of the MACRO, which accepts arguments
-                                 // i can assert that frame.pos == 0.
-                                 // In this case i can replace the MACRO with the cursor
-                                 assert(frame.pos == 0);
-
-                                 cursor.remove();
-                                 parent.remove();
-                                 gfuther.replace(cursor);
-
-                                 frames.pop();
-                               }
-                           
-                           }
-                       }
-                   }
-                 else // the grand futher is math
-                   {
-                     // nothing to do...i think
-                     assert(frames.empty());
+}
+
+std::string
+TPushParser::do_drop_groupId(bool special)
+{
+  // if we are here, the cursor's parent is a group with Id
+  assert(cursor.parent() && cursor.parent().isG() && cursor.parent().hasId());
+  TNode parent = cursor.parent();
+
+  // we have to take different actions based on if the cursor has a preceding 
+  // element or not
+  TNode prev = cursor.prev();
+  if (prev)
+    {
+      // the cursor has a preceding element, so we try to remove it
+      if (special) return drop_prev(special);
+      else
+        {
+          std::string str = drop_prev(special);
+
+          // We control if the group has to be removed, because the cursor 
+          // might be the only element of the group.
+          // But we have to be careful, because drop_prev could change the TML tree 
+          // more than we think...parent could no longer exist! 
+          parent = cursor.parent();
+          if ((parent.first() == cursor) && parent.isG() && parent.hasId())
+           rgreplace_father();
+      
+          return str;
+       }
+    }
+  else
+    {
+      // the cursor has no preceding elements, so we have to remove the 
+      // group.
+      if (special)
+        {
+         parent.replace(cursor);
+         return "";
+       }
+      else
+        {
+         rgreplace_father();
+         // we have to re-start the process, because it' a graphical deletion
+         return do_drop(special);
+       }
+    }
+
+} // end of method do_drop_groupId()
+
+std::string
+TPushParser::do_drop_phantom_group(bool special)
+{
+  // if we are here, the cursor MUST be a child of a 
+  // phantom group.
+  assert(cursor.parent() && cursor.parent().isG() && !cursor.parent().hasId());
+
+  TNode parent = cursor.parent();
+
+  // now we have to control if the cursor has a preceding element or not
+  TNode prev = cursor.prev();
+  if (prev)
+    {
+      if (parent.parent() && parent.parent().isC())
+        {
+         // there is a frame in the stack
+         assert(!frames.empty());
+         if (frames.top().entry.pattern.size())
+           {
+             Frame& frame = frames.top();
+             if (special)
+               {
+                 // we are in a delimited argument. If the user has inserted a proper subset of the 
+                 // delimiters'sequence, we start to remove the previous delimiter. Start to remove 
+                 // a delimiter means that that delimiter must be removed from the count of inserted delimiters.
+                 // It means that we have to decrement the member pos.
+                 if (frame.entry.pattern[frame.pos].category != TToken::PARAMETER)
+                   {
+                     std::string del = frame.entry.pattern[frame.pos].value;
+                     frame.pos--;
+                     return "\\" + del;
                    }
                }
              else
-               { 
-                 // the parent is a group with id and has no elements other than cursor
-                 // so we replace it with the cursor.
-                 rgreplace_futher();
-
-                 // i have to re-start the process, because it' a graphical delete
-                 do_gdelete();
+               {
+                 // we are in a delimited argument. If the user has inserted a proper subset of the delimiters'sequence, 
+                 // we have to remove the portion the user has inserted.
+                 while (frame.entry.pattern[frame.pos].category != TToken::PARAMETER) frame.pos--;
                }
            }
        }
-      else if (parent.isC())
+      
+      // the cursor has a preceding element, so we try to remove it
+      std::string str = drop_prev(special);
+
+      if (special) return str;
+      else
+        {
+          // now we have to control the parent, to handle the case of primes. But we have returned from a drop_prev(), which
+          // could change the TML tree. So not asssuming that cursor's parent is unchanged is convenient.
+          parent = cursor.parent();
+          if (parent.isG() && !parent.hasId() && (parent.size() == 1) && parent.parent().isSp())
+            {
+             // in this case the drop_prev has removed the only element preceding the cursor.
+             // Since the phantom group is an sp's child, the user has removed all \' in the 
+             // phantom group.
+             // Now we have some possibilities:
+             //   - we can replace the phantom group with the cursor, giving the user the chance to insert a new 
+             //     exponent
+             //   - we can remove the phantom group and the sp element, recreating the state before the user inserted the first
+             //     prime.
+             // At the moment we implement the second one.
+             assert(parent.parent().size() == 2);
+             TNode gparent = parent.parent();
+             TNode base = gparent.first();
+             cursor.remove();
+             parent.remove();
+             gparent.replace(base);
+             // now base's parent is no more gparent
+             base.parent().append(cursor);
+         
+             return str;
+           }
+          else if (parent.isG() && !parent.hasId() && parent.parent().isSp())
+            {
+             // in this case we have to place the cursor after the sp element
+             cursor.remove();
+             assert(parent.parent().parent());
+             parent.parent().parent().append(cursor);
+             return str;
+           }
+          else return str;
+       }
+    }
+  else
+    {
+      // in this case the cursor is the only element of the phantom group,
+      // so we have to remove it. But, a phantom group has a special role, 
+      // so we have to control the grand father of the cursor.
+      TNode gfather = parent.parent();
+      if (!gfather)
+        {
+         // If here, the TML tree is in an inconsistent state
+         logger.error("TML tree in a inconsistent state");
+         return "";
+       }
+      else if (gfather.isC())
         {
-         // being here means that there is an open frame for the control element
-         // and this element is closed at either side (no leftOpen no rightOpen)
-         // and the MACRO was waiting for a non delimited argument, so 
-         // we can assert that frame.entry.pattern.size() >= 1
+         // in this case the phantom group is child of a MACRO.
+         // We have to control the nature of this MACRO.
          assert(!frames.empty());
-         Frame& frame = frames.top();
-
-         assert(frame.entry.pattern.size() >= 1);
-
-         cout << "do_gdelete: frames.top().pos = " << frames.top().pos << endl;
+          Frame& frame = frames.top();
 
-         TNode prev = cursor.prev();
+         // this variable is useful in a special deletion
+         std::string macro_name = gfather.nameC();
+         
+         if (frame.entry.leftOpen && frame.entry.rightOpen)
+           {
+             // in this case, the cursor'parent is in the second and last child 
+             // of the MACRO. We can assert that the grand father has two 
+             // children, which are both phantom groups
+             assert(gfather.size() == 2);
+             assert((gfather.last() == parent) && (gfather.first().isG() && !gfather.first().hasId()));
+             assert(frame.pos == 0);
+             
+             TNode ggfather = gfather.parent();
+             assert(ggfather);
+             cursor.remove();
+             parent.remove();
+             // we have to replace the gfather with the elements of its first child, but this group may have no 
+             // children.
+             if (gfather.first().size())
+               {
+                 gfather.replace(gfather.first().first(), TNode());
+                 ggfather.append(cursor);
+               }
+             else
+               {
+                 // in this case, the MACRO has to be replaced with the cursor
+                 gfather.first().remove();
+                 gfather.replace(cursor);
+               }
+             // now we have the situation preceding the insertion of the leftOpen and rightOpen MACRO.
+             // this MACRO no longer exists.
+             frames.pop();
 
-         if (!prev)
+             if (special) return "\\" + macro_name;
+             else
+               {
+                 // to give a standard behavior to the graphical deletion, we call the do_drop.
+                 return do_drop(special);
+               }
+           }
+         else if (frame.entry.rightOpen)
            {
-             // in this case we can replace the MACRO with the cursor 
-             // and pop the stack and we can assert that frame.pos == 0
+             // the user has inserted a rightOpen MACRO, and now, this MACRO has no children (excluding the 
+             // phantom group), so we remove the MACRO. 
+             // We can assert that cursor's parent is the only child of the MACRO
+             assert(gfather.size() == 1);
              assert(frame.pos == 0);
-             cursor.remove(); // it should not be necessary, but i'm not shure
-             parent.replace(cursor);
+             cursor.remove();
+             parent.remove();
+             gfather.replace(cursor);
+             
+             // now we have the situation preceding the rightOpen MACRO, so we have to pop the frame
              frames.pop();
+
+             if (special) return "\\" + macro_name;
+             else
+               {
+                 // to give a standard behavior to the graphical deletion, we call the do_drop.
+                 return do_drop(special);
+               }
+             
            }
-         else
+         else if (frame.entry.leftOpen)
+           {
+             // this situation will never occur.
+             logger.error("the parser has generated a wrong TML tree");
+             return "";
+           }
+         else if (!frame.entry.pattern.empty())
            {
-             // in this case the cursor has a preceding element 
-             // and there are differnt things based on the nature 
-             // of the prev: if it's a phantom group do something,
-             // else do something else
-             if (prev.isG() && !prev.hasId())
+             // the MACRO accepts arguments, and the phantom group in which 
+             // the cursor is, rappresents a delimited argument.
+             // We have to control if the cursor's parent has a preceding element, 
+             // or not.
+             TNode uncle = parent.prev();
+             if (!uncle)
                {
-                 // in this case we have to append the cursor 
-                 // to the prev and decrement frame.pos of two units
-                 // because the prev is a delimited argument and the 
-                 // delimiter is inserted. So we ideally remove this delimiter
+                 // the parent is the only element of the MACRO. 
+                 // we can assert that frame.pos == 0.
+                 // In this case we can replace the MACRO with the cursor
+                 assert(frame.pos == 0);
                  cursor.remove();
-                 prev.append(cursor);
-                 frame.pos -=2;
+                 parent.remove();
+                 gfather.replace(cursor);
+                 frames.pop();
+
+                 if (special) return "\\" + macro_name;
+                 else
+                   {
+                     // once we have replaced the empty macro with the cursor, we can remove
+                     // something else
+                     return do_drop(special);
+                   }
                }
              else
                {
-                 // the prev is an non delimited argument, so we try to 
-                 // remove it.
-                 gdelete_prev();
-               }
+                 // the parent has a preceding element. Now we have 
+                 // to control if the uncle is a delimited argument or not.
+                 if (uncle.isG() && !uncle.hasId())
+                   {
+                     // cursor's uncle is a delimited argument
+                     cursor.remove();
+                     parent.remove();
+                     uncle.append(cursor);
+                     if (special)
+                       {
+                         // we have to start removing the last delimiter of the delimited 
+                         // argument.
+                         std::string last_del = frame.entry.pattern[frame.pos - 1].value;
+                         frame.pos = frame.pos - 2;
+                         return "\\" +  last_del; 
+                       }
+                     else
+                       {
+                         // the  uncle is a delimited argument. So we have to ideally
+                         // remove the sequence of delimiters.
+                         assert(frame.pos > 1);
+                         unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
+                         assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
+                         assert(sequence_length);
+                         // sequence_length is the length of the delimiters sequence which separates
+                         // the current parameter and the previous parameter
+                         frame.pos = frame.pos - sequence_length - 1;
+
+                         // once removed the sequnce of delimiters, we can start to remove the actual 
+                         // parameter. We can call the do_drop_phantom_group() because a delimited argument
+                         // is always a phantom group's child
+                         return do_drop_phantom_group(special);
+                       }
+                   }
+                 else
+                   {
+                     // the uncle is a not delimited argument, so we try to remove it.
+                     cursor.remove();
+                     parent.replace(cursor);
+                     parent = cursor.parent(); // we update the parent (it should be the MACRO)
+                     assert(parent.isC());
+
+                     // now we try to remove the uncle (now it' the preceding element)
+                     return drop_prev(special);
+                   }
+               } // this is the else's end, that handles the case in which an uncle exists
+           } // end of if (!frame.entry.pattern.empty())
+         else
+           {
+             // the entry has no arguments, is not rightOpen and is not leftOpen.
+             logger.error("TML tree in a strange state");
+             return "";
            }
-       }
-      else if (parent.is("sp") || parent.is("sb"))
+       } // end of if (gfather.isC())
+      else if (gfather.is("cell"))
         {
-         // being here means that a prev MUST exist 
-         // and that there is only an element preceding the cursor.
-         // A sp's (or sb's) MUST NOT be a MACRO
+         // A table is a control sequence, so there is a frame in the stack
+         assert(!frames.empty());
+         assert(frames.top().pos == 0);
+         assert(frames.top().entry.table == 1);
+         
+         // a cell MUST be a row's child, which in turn is a table's child 
+         assert(gfather.parent() && gfather.parent().is("row") && gfather.parent().parent());
 
-         assert(parent.size() == 2);
-         assert(parent.parent() && !parent.parent().isC());
+         // this variable is useful to handle the special deletion
+         std::string table_name = gfather.parent().parent().nameC();
+         
+         TNode row = gfather.parent();
          
-         TNode prev = cursor.prev();
+         // in this case the cell has no element, so the user wants to delete this cell.
+         TNode prev_cell = gfather.prev();
          cursor.remove();
-         if (prev.isG() && !prev.hasId() && (prev.size() == 0))
-           {
-             prev.remove();
-             parent.replace(cursor);
+         parent.remove();
+         gfather.remove();
+         // now the cell no longer exists
 
-             // now, cursor should be the only parent's child
-             assert(cursor.parent().size() == 1);
-
-             if (cursor.parent().isG() && cursor.parent().hasId()) rgreplace_futher();
-           }
+         if (!prev_cell)
+           {
+             // in this case, the cell was the only cell in the row.
+             // So, we assume that the user wants to delete the entire row.
+             TNode table = row.parent();
+             TNode prev_row = row.prev();
+             row.remove();
+             
+             if (!prev_row)
+               {
+                 if (special)
+                   {
+                     // Since there was a cell (and a row), the user has typed a "{" to 
+                     // We ideally remove this character.
+                     table.append(cursor);
+                     return "";
+                   }
+                 else
+                   {
+                     // the row was the only child of the table. 
+                     // so we have to delete the entire table
+                     assert(table.parent());
+                     TNode parent_table = table.parent();
+                     table.remove();
+                     frames.pop();
+                     parent_table.append(cursor);
+                     return "";
+                   }
+               }
+             else
+               {
+                 // there are other rows (one or more)
+                 assert(prev_row.is("row"));
+                 assert(prev_row.last());
+                 TNode last_cell = prev_row.last();
+                 assert(last_cell.is("cell"));
+                 assert(last_cell.size() == 1);
+                 assert(last_cell.first().isG() && !last_cell.first().hasId());
+                 last_cell.first().append(cursor);
+                 // Since cells and rows are separated by spaces and CRs 
+                 // (and the user can see this spaces and CRs), a special deletion 
+                 // is equivalent to a normal deletion
+                 return "";
+               }
+           } // end of if (!prev_cell)
          else
            {
-             assert(prev.hasId());
-             parent.replace(prev);
-             prev.parent().append(cursor);
-             // now prev should have a preceding element
-             assert(cursor.parent().size() > 1);
-           } 
+             // being here means that there is a previous cell,
+             // so we append the cursor to group.
+             assert(prev_cell.size() == 1);
+             assert(prev_cell.first().isG() && !prev_cell.first().hasId());
+             prev_cell.first().append(cursor);
+             return "";
+           }
+       } // end of if (gfather.is("cell"))
+      else if (gfather.isSp())
+        {
+         // we cannot be here because a phantom group can be a Sp child only 
+         // in two cases. If the user has typed somethong like:
+         // $^
+         // the cursor is not phantom group's child.
+         // If the user has typed somethong like
+         // ..''
+         // In this case the sequence of ' is placed in a phantom group, 
+         // which becomes the exponent of the script. But, the cursor is 
+         // always outside the phantom group
+         logger.error("TML tree in a strange state");
+         return "";
+       }
+      else if (gfather.is("math"))
+        {
+         // in this case we ignore the user's will of deleting
+         // but we could also decide to remove the math mode.
+         logger.warning("nothing to delete");
+         return "";
        }
       else
         {
-         // not handled: no control for tables, ...
+         // cursor's grand father is undefined
+         logger.error("TML tree is in an unknown state");
+         return "";
        }
+    } // end of the else of the if (prev)
+
+}
+
+
+std::string
+TPushParser::do_drop(bool special)
+{
+  // we have to handle the case in wich the cursor has a parent or not
+  if (!cursor.parent())
+    {
+      // it's not a good situation...at the moment we do not take actions
+      logger.error("TML tree not well structured");
+      return "";
     }
   else
     {
-      // the cursro has no parent!!!
-      // do nothing?
-      // emit an error? and if we want to emit an error, in which way?
-    }
-
-} // end of method
+      // a parent exists. We have to take differnt actions, based on the nature of 
+      // the parent
+      TNode parent = cursor.parent();
+      if (parent.is("math"))
+        {
+         // we ca do two thing...we can remove the math mode (it implies controlling the display attribute), we can do nothing
+         // At the moment, the user's will of deleting is simply ignored
+         logger.warning("nothing to delete");
+         return "";
+       }
+      else if (parent.isG())
+        {
+         // the cursor's parent is a group. We have to control if it's a phantom group or not
+         if (parent.hasId())
+           {
+             return do_drop_groupId(special);
+           }
+         else
+           {
+             return do_drop_phantom_group(special);
+           }
+       } // end of parent is group
+      else if (parent.isC())
+        {
+         return do_drop_macro(special);
+       } // end of parent is a MACRO
+      else if (parent.isSp() || parent.isSb())
+        {
+         return do_drop_script(special);
+       } // end of parent is sp or sb
+    } // end of the else which consider the case in which parent exists
+  
+} // end of method do_drop
 
-void
+bool
 TPushParser::process(const TToken& token)
 {
   switch (token.category)
     {
-    case TToken::BEGIN: do_begin(); break;
-    case TToken::END: do_end(); break;
-    case TToken::SHIFT: do_shift(); break;
-    case TToken::ALIGN: do_align(); break;
-    case TToken::EOL: do_eol(); break;
-    case TToken::PARAMETER: do_parameter(token.value); break;
-    case TToken::SUPERSCRIPT: do_superscript(); break;
-    case TToken::SUBSCRIPT: do_subscript(); break;
-    case TToken::SPACE: do_space(token.value); break;
-    case TToken::LETTER: do_letter(token.value); break;
-    case TToken::DIGIT: do_digit(token.value); break;
-    case TToken::OTHER: do_other(token.value); break;
-    case TToken::ACTIVE: do_active(token.value); break;
-    case TToken::COMMENT: do_comment(); break;
-    case TToken::CONTROL: do_control(token.value); break;
-    case TToken::GDELETE: do_gdelete(); break;
+    case TToken::BEGIN: return do_begin();
+    case TToken::END: return do_end();
+    case TToken::SHIFT: return do_shift();
+    case TToken::ALIGN: return do_align();
+    case TToken::EOL: return do_eol();
+    case TToken::PARAMETER: return do_parameter(token.value);
+    case TToken::SUPERSCRIPT: return do_superscript();
+    case TToken::SUBSCRIPT: return do_subscript();
+    case TToken::IGNORABLE_SPACE: return do_ignorablespace(token.value);
+    case TToken::SPACE: return do_space(token.value);
+    case TToken::LETTER: return do_letter(token.value);
+    case TToken::DIGIT: return do_digit(token.value);
+    case TToken::OTHER: return do_other(token.value);
+    case TToken::ACTIVE: return do_active(token.value);
+    case TToken::COMMENT: return do_comment();
+    case TToken::CONTROL: return do_control(token.value);
     }
 }
 
 void
 TPushParser::push(const TToken& token)
 {
-  cerr << "TPushParser::push " << token.value << " (cat: " << token.category << ")" << endl;
-
-  if (token.category == TToken::GDELETE)
-    {
-      cout << "push: i have to process a token with category member = GDELETE" << endl;
-      process(token);
-    }
-  else
-    {
-
   TNode parent = cursor.parent();
   // If the cursor has no parent then it is detached from the editing
   // tree, which means this token will be ignored
@@ -967,38 +1750,38 @@ TPushParser::push(const TToken& token)
            assert(frame.entry.pattern[frame.pos + 1].category != TToken::PARAMETER);
            if (frame.entry.pattern[frame.pos + 1] == token)
              {
-               // The token matches with the delimiter of the argument.
-               cursor.remove();
-
-               // If the phantom group has just one child, it is not necessary
-               // and can be removed. However, this would complicate
-               // all the code that follows, so given it is a rare case we
-               // leave it alone
-               // if (parent.size() == 1) parent.replace(parent[0]);
+               // The token matches with a delimiter of the argument, 
+               // hence we increment the frame.pos
+               frame.pos++;
 
-               // Eat both the argument and its delimiter
-               frame.pos += 2;
-               if (frame.pos == frame.entry.pattern.size())
-                 {
-                   // This token has completed the entry
-                   advance(parent.parent());
-                 }
-               else if (frame.entry.paramDelimited(frame.pos))
+               if (frame.entry.lastDelimiter(frame.pos))
                  {
-                   // For the next is a delimited argument we have to place
-                   // a suitable phantom group with the cursor inside
-                   TNode g = doc.createG();
-                   parent.parent().append(g);
-                   g.append(cursor);
+                   // this delimiter is the last one for the argumet, 
+                   // so the argument is completed
+                   cursor.remove();
+                   advance(parent);
                  }
-               else
-                 parent.parent().append(cursor);
              }
            else
              {
-               // Delimiter mismatch. Since we are parsing a delimited
-               // argument we just process the token
-               process(token);
+               // Delimiter mismatch.
+               if (frame.entry.pattern[frame.pos].category != TToken::PARAMETER)
+                 {
+                   // in this case, there is a sequence of delimiters that delimitates
+                   // the argument, and the user has correctly inserted a portion of this 
+                   // sequence, but now has inserted a wrong delimiter.
+                   // Here, there are some possibilities:
+                   //   - ignore the token, and wait for the correct delimiter
+                   //   - ignore the token, wait for the correct delimiter and emit an error
+                   // At the moment, we implement the second possibily
+                   logger.warning("it's not the correct delimiter...you have to type '" + frame.entry.pattern[frame.pos + 1].value + "'");
+                 }
+               else
+                 {
+                   // in this case, the sequence of delimiters is composed of one 
+                   // delimiter. It means that we have to process the token
+                   process(token);
+                 }
              }
          }
        else
@@ -1018,21 +1801,11 @@ TPushParser::push(const TToken& token)
        Frame& frame = frames.top();
        assert(frame.pos < frame.entry.pattern.size());
 
-       cout << "push: il valore di pos del frame inserito prima e': " << frame.pos <<  endl;
        if (frame.entry.pattern[frame.pos].category == TToken::PARAMETER)
          {
            // As by the TeX parsing rules of undelimited parameters,
            // empty spaces are ignored
-           if (token.category != TToken::SPACE)
-             {
-               // We need to increase the frame position here, becase inside
-               // process the function advance will be called. At that point
-               // it will be important for the parser to know that the entry
-               // has been completed in order to place the cursor correctly
-               // in the next position
-               frame.pos++;
-               process(token);
-             }
+           if ((token.category != TToken::SPACE) && (token.category != TToken::IGNORABLE_SPACE)) process(token);
          }
        else if (frame.entry.pattern[frame.pos] == token)
          {
@@ -1048,68 +1821,132 @@ TPushParser::push(const TToken& token)
                g.append(cursor);
              }
            else
-             advance(parent);
+             {
+               cursor.remove();
+               advance(parent);
+             }
          }
        else
          {
            // There is a mismatch. Emit an error and ignore the token?
-           cerr << "ERROR: token ignored: " << token.value << " (cat: " << token.category << ")" << endl;
+           logger.warning("ignored token: " + token.value);
          }
-
       }
     else
       process(token);
   else
     {
-      cout << "ignored token" << endl;
+      logger.warning("ignored token");
     }
 
-  } // this end corresponds to the else of the if (token.category == GDELETE)
-
-  if (listener) listener->callback(doc);
+  if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
+}
 
-  if (frames.empty()) cout << "stack vuoto" << endl;
-  else cout << "stack non vuoto" << endl;
+std::string
+TPushParser::drop(bool special)
+{
+  std::string str = do_drop(special);
+  if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
+  return str;
 }
 
 void
 TPushParser::advance(const TNode& node)
 {
   assert(node);
-  TNode parent = node.parent();
-  if (!parent)
-    ; // nothing to do, the cursor is not in the document any more
-  else if (parent.isG())
+  
+  if (!node.parent())
+    {
+      // this is an error
+      logger.error("wrong TML tree");
+    }
+  else if (node.parent().isG())
     {
       TNode next = node.next();
       if (next) next.insert(cursor);
-      else parent.append(cursor);
+      else node.parent().append(cursor);
     }
-  else if (parent.isC())
+  else if (node.parent().isC())
     {
-      if (node.next())
-       ; // cursor removed
-      else
-       {
-         Frame& frame = frames.top();
-         if (frame.pos == frame.entry.pattern.size())
+      assert(!frames.empty());
+      if ((frames.top().pos + 1 == frames.top().entry.pattern.size()) || (frames.top().entry.pattern.empty()))
+        {
+         // we are here when we have a right open macro, or the inserted element is the last one
+         if (frames.top().entry.rightOpen)
            {
+             // we have to remove the frame from the stack
              frames.pop();
-             advance(parent);
+             advance(node.parent().parent());
            }
          else
-           parent.append(cursor);
+           {
+             frames.pop();
+             advance(node.parent());
+           }
+       }
+      else if (frames.top().entry.paramDelimited(frames.top().pos + 1))
+        {
+         // the next argument is delimited, so we have to create a phantom group
+         TNode g = doc.createG();
+         g.append(cursor);
+         node.parent().append(g);
+         frames.top().pos++;
+       }
+      else
+        {
+         // the next argumet is not delimited, so we have to append the cursor
+         // to the MACRO
+         node.parent().append(cursor);
+         frames.top().pos++;
        }
     }
-  else if (parent.is("math"))
-    ; // we are done
-  else
-    advance(parent);
+  else advance(node.parent());
 }
 
 void
-TPushParser::setCursor(const std::string& c)
+TPushParser::setCursorHint(const std::string& c)
+{
+  if (cursor["val"] != c)
+    {
+      cursor["val"] = c;
+      if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
+    }
+}
+
+bool
+TPushParser::hideCursor()
+{
+  if (hiddenCursor++ == 0)
+    {
+      cursor["visible"] = "0";
+      if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
+      return true;
+    }
+  else
+    return false;
+}
+
+bool
+TPushParser::showCursor()
 {
-  cursor["val"] = c;
-  if (listener) listener->callback(doc);
+  if (hiddenCursor > 0 && --hiddenCursor == 0)
+    {
+      cursor["visible"] = "1";
+      if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
+      return true;
+    }
+  else
+    return false;
+}
+
+bool
+TPushParser::thaw()
+{
+  if (APushParser::thaw() && factory && doc.dirtyNode())
+    {
+      factory->documentModified(doc);
+      return true;
+    }
+  else
+    return false;
 }