X-Git-Url: http://matita.cs.unibo.it/gitweb/?a=blobdiff_plain;f=helm%2FDEVEL%2Fmathml_editor%2Fsrc%2FTPushParser.cc;h=0c96fe43968c94be52a1fdcffac385012d683ebb;hb=97c2d258a5c524eb5c4b85208899d80751a2c82f;hp=836be35a894eeacd5c11727e5aad6d01d794a6ae;hpb=281107dfb0a5531ac13c9e183f39c05c65813e86;p=helm.git diff --git a/helm/DEVEL/mathml_editor/src/TPushParser.cc b/helm/DEVEL/mathml_editor/src/TPushParser.cc index 836be35a8..0c96fe439 100644 --- a/helm/DEVEL/mathml_editor/src/TPushParser.cc +++ b/helm/DEVEL/mathml_editor/src/TPushParser.cc @@ -1,3 +1,29 @@ +/* This file is part of EdiTeX, an editor of mathematical + * expressions based on TeX syntax. + * + * Copyright (C) 2002-2003 Luca Padovani , + * 2003 Paolo Marinelli . + * + * 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 + */ + +#include #include "ALogger.hh" #include "TPushParser.hh" @@ -5,38 +31,56 @@ TPushParser::TPushParser(ALogger& l, const TDictionary& d) : APushParser(l), dictionary(d) { - reset(); + init(); } TPushParser::TPushParser(ALogger& l, AMathMLFactory& f, const TDictionary& d) : APushParser(l, f), dictionary(d) { - reset(); + init(); } TPushParser::~TPushParser() { } +void +TPushParser::init() +{ + cursor = doc.create("cursor"); + cursor["visible"] = "1"; + hiddenCursor = 0; + reset(); +} + void TPushParser::reset() { - APushParser::reset(); nextId = 1; - cursor = doc.create("cursor"); - cursor["id"] = "I0"; - doc.clearDirty(); + 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(); @@ -56,6 +100,7 @@ TPushParser::do_begin() cursor.replace(g); g.append(cursor); } + return true; } bool @@ -117,7 +162,7 @@ TPushParser::correctBrace() return ok; } -void +bool TPushParser::do_end() { TNode parent = cursor.parent(); @@ -126,6 +171,7 @@ 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")) { @@ -135,6 +181,7 @@ TPushParser::do_end() assert(row && row.is("row")); assert(row.parent()); advance(row); + return true; } else if (parent && parent.isG() && !parent.hasId() && parent.parent() && !parent.parent().is("math")) { @@ -159,24 +206,27 @@ TPushParser::do_end() { // the '}' is not correct logger.warning("nothing to close"); + return false; } else { cursor.remove(); advance(parent); + return true; } - } else { - logger.error("closing brace ignored"); + logger.warning("ignored closing brace"); + return false; } } else { - // at the moment, a phantom group with cursor can be a MACRO's child or a cell's child, and these cases + // 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 @@ -184,52 +234,12 @@ TPushParser::do_end() // 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 -TPushParser::do_end() -{ - TNode parent = cursor.parent(); - if (parent && parent.isG() && parent.hasId()) - { - // normal closing brace for an explicitly open group - cursor.remove(); - advance(parent); - } - 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(row); - } - else if (parent && parent.isG() && !parent.hasId() && parent.parent() && !parent.parent().is("math")) - { - // closing brace for a right-open macro (like \over) - // It's not sure that we want to exit from a phantom group with a - // '}', because, to enter in the phantom group the user didn't insert a - // '{'. - cursor.remove(); - advance(parent); - } - else - { - // In this case, there is a redundant '}', so I think we can ignore it and - // emit an error - logger.warning("There is so no corresponding'{'"); - //assert(0); - } -} -*/ - -void +bool TPushParser::do_shift() { TNode parent = cursor.parent(); @@ -241,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")) { @@ -252,12 +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") @@ -265,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 { - logger.error("parser: math shift"); + logger.warning("not allowed here"); + return false; } } -void +bool TPushParser::do_align() { TNode parent = cursor.parent(); @@ -294,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")) { @@ -308,26 +326,31 @@ TPushParser::do_align() TNode g = doc.createG(); cell.append(g); g.append(cursor); + return true; } else { - logger.error("alignment tab used outside matrix"); + 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(); @@ -341,6 +364,7 @@ TPushParser::do_subscript() cursor.replace(elem); elem.append(g); elem.append(cursor); + return true; } else { @@ -348,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") logger.error("already under"); - 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(); @@ -371,6 +409,7 @@ TPushParser::do_superscript() cursor.replace(elem); elem.append(g); elem.append(cursor); + return true; } else { @@ -378,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") logger.error("already over"); - 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&) { - logger.debug("do_space"); + 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()) @@ -435,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"); @@ -447,52 +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 { // is it an error? - logger.error("parser: you have to type one identifier before the ''"); + 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(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(); @@ -512,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(); @@ -531,6 +605,7 @@ TPushParser::do_control(const std::string& name) t["name"] = name; cursor.replace(t); advance(t); + return true; } break; case TDictionary::OPERATOR: @@ -539,6 +614,7 @@ TPushParser::do_control(const std::string& name) t["name"] = name; cursor.replace(t); advance(t); + return true; } break; case TDictionary::NUMBER: @@ -547,50 +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); - - if (entry.leftOpen && entry.rightOpen) + if (parent.isG()) { - 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()) - { - 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)); - if (entry.paramDelimited(0)) { TNode g = doc.createG(); @@ -602,63 +677,102 @@ TPushParser::do_control(const std::string& name) } else { - // error, but we could handle this very easily - logger.error(" parser:but we could handle this easily"); - } + // 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: { - logger.error("parser: using undefined macro " + name); + 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_token() +std::string +TPushParser::drop_prev_token(bool special) { assert(cursor.prev()); assert(cursor.parent()); TNode prev = cursor.prev(); - assert(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); - // 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 ((prev.is("i")) || (ucs4val.length() <= 1) || prev.element().hasAttribute("name")) + if (cursor.parent().isC()) { - cursor.remove(); - prev.replace(cursor); - - if (cursor.parent().isC()) - { - // 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--; - } + // 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--; } - else + + if ((ucs4val.length() > 1)) { - ucs4val.erase(ucs4val.length() - 1, 1); - prev.element().setAttribute(DOM::GdomeString("val"), DOM::GdomeString(ucs4val)); + 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]) + { + 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 + { + // 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 : ""; + } } - + else if (macro && special) return "\\" + utf8name; + else return ""; } -void -TPushParser::gdelete_prev_script() +std::string +TPushParser::drop_prev_script(bool special) { // this method deletes an sp or an sb preceding the cursor assert(cursor.prev()); @@ -667,38 +781,73 @@ TPushParser::gdelete_prev_script() assert(prev.is("sp") || prev.is("sb")); cursor.remove(); prev.append(cursor); - // we can invoke the gdelete_prev, because a sp (sb) MUST have two children - gdelete_prev(); + // 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()) + { + // 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); } -void -TPushParser::gdelete_prev_group() +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); - - // a group could have no children, so the gdelete_prev is not appropriate - // so, this method is not equivalent to the one above - do_gdelete(); + + if (parent.isC() && prev.hasId()) + { + // 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--; + } + + if (special) return ""; + else + { + // 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); + } } -void -TPushParser::gdelete_prev_macro() +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()) { - // We can assume that the user want to completely delete the undefined macro + // 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 { @@ -711,7 +860,7 @@ TPushParser::gdelete_prev_macro() if (entry.rightOpen) { // In this fragment of code we also handle the leftOpen && rightOpen MACRO. - // if the control element is rightOpen, the cursor should be placed after + // 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. @@ -722,19 +871,23 @@ TPushParser::gdelete_prev_macro() cursor.remove(); prev.last().append(cursor); - - // the gdelete_prev is not appropriate, because the last child of the MACRO could have no children - do_gdelete_phantom_group(); + + 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) { // 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. - // At the moment, we don't remove the last child of the phantom group, but - // it's not clear if it's the correct behavior of the graphical deleting. - // To delete it, just remove the comment of the last instruction of the - // if (g.size()) block. + // 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()); @@ -746,7 +899,9 @@ TPushParser::gdelete_prev_macro() // TNode::replace. g.remove(); prev.replace(g.first(), TNode()); - //gdelete_prev(); + parent.append(cursor); + if (special) return "\\" + macro_name; + else return do_drop(special); } else { @@ -754,6 +909,14 @@ TPushParser::gdelete_prev_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()) @@ -767,17 +930,37 @@ TPushParser::gdelete_prev_macro() // frome the one in which it's a not delimited argument. if (prev.last().isG() && !prev.last().hasId()) { - // 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)); + 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 { @@ -786,33 +969,36 @@ TPushParser::gdelete_prev_macro() cursor.remove(); if (entry.table == 1 && prev.last().is("row")) { - // in this case the cursor should be appended to the group associated to + // 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); } - // we push a frame in the stack with a correct value of member pos. - // This correct value is the size of the pattern, because we have to START to delete - // a MACRO. It means that all of the MACRO's arguments have been inserted. - frames.push(Frame(entry, entry.pattern.size())); - - if (cursor.prev()) - { - // in this case we try to remove the last child of the MACRO. - gdelete_prev(); - } - else - { - // this block of code will never be executed, because the MACRO accepts arguments, - // so a MACRO's child MUST exist. But, we can handle this case, emitting a warning - logger.warning("Parser: the TML tree is in a strange state, but a good state will be generated"); - do_gdelete(); - } } // end of the else of the if (prev.last().isG() && !prev.last().hasId()) } // end of if (!entry.pattern.empty()) @@ -824,47 +1010,63 @@ TPushParser::gdelete_prev_macro() 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 } -void -TPushParser::gdelete_prev() +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 deleting. + // 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.is("i") || prev.is("o") || prev.is("n")) + if (prev.isT()) { - gdelete_prev_token(); + return drop_prev_token(special); } else if (prev.isSp() || prev.isSb()) { - gdelete_prev_script(); + return drop_prev_script(special); } else if (prev.isG()) { - gdelete_prev_group(); + return drop_prev_group(special); } else if (prev.isC()) { // here, we also treat the case in which the MACRO is a table - gdelete_prev_macro(); + return drop_prev_macro(special); } else { // not handled. Future cases... + return ""; } } // end of method void -TPushParser::rgreplace_father(void) +TPushParser::rgreplace_father() { // this method MUST only be invoked, when the cursor // is the only child of a group with id. This function @@ -882,21 +1084,10 @@ TPushParser::rgreplace_father(void) parent.replace(cursor); parent = cursor.parent(); } - - if (parent.isC()) - { - // in this case we have removed a MACRO's child. - // So, we have to update the member pos of the frame in the stack - // We can assert that this MACRO accepts arguments. - assert(!frames.empty()); - Frame& frame = frames.top(); - assert(frame.pos > 0); - frame.pos--; - } } -void -TPushParser::do_gdelete_script() +std::string +TPushParser::do_drop_script(bool special) { // 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 @@ -916,35 +1107,61 @@ TPushParser::do_gdelete_script() { // 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 type something like this + // This situation occurs when the user had typed something like this // $....{}^ // or this // $^ // or this // $...{^ - prev.remove(); - parent.replace(cursor); - - // if the new parent is a group with Id and the cursor is the only - // element of this group, we have to remove the group. These controls are made - // in the method rgreplace_father(). - if (cursor.parent().isG() && cursor.parent().hasId()) rgreplace_father(); + // + 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, - // and the cursor has to be placed after the prev. - assert(prev.hasId()); + // 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_gdelete_script +} // end of method do_drop_script -void -TPushParser::do_gdelete_macro() +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 @@ -953,6 +1170,9 @@ TPushParser::do_gdelete_macro() // we can assert that frame.entry.pattern.size() >= 1 assert(cursor.parent() && cursor.parent().isC()); TNode parent = cursor.parent(); + + // this string is useful iff we have a special deletion. + std::string macro_name = parent.nameC(); assert(!frames.empty()); Frame& frame = frames.top(); @@ -965,25 +1185,17 @@ TPushParser::do_gdelete_macro() { // 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. - // Being here also means that the MACRO is waiting for the first argument - // (which is not delimited), but we don't mind about it. assert(frame.pos == 0); + parent.replace(cursor); frames.pop(); - // if the new parent is a group with Id, and has no elements other than the - // cursor, we can remove it, because it' a graphical deleting - if (cursor.parent() && cursor.parent().isG() && cursor.parent().hasId()) - rgreplace_father(); - else if (cursor.parent().isC()) + if (special) return "\\" + macro_name; + else { - // We have assumed that a MACRO cannot be a MACRO's child. - // At the moment, this assumption is valid, but in a future - // it might be false. - assert(!frames.empty()); - Frame& frame = frames.top(); - assert(frame.pos > 0); - frame.pos--; + // Since the macro had no children and this is a graphical deletion, we try + // to remove something else + return do_drop(special); } } else @@ -998,25 +1210,42 @@ TPushParser::do_gdelete_macro() cursor.remove(); prev.append(cursor); assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size()); - - // 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 (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 + { + // 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; + + // 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 a something that - // a simple delete cannot remove completely - gdelete_prev(); + // We "try", because the prev might be something that + // a simple deletion cannot remove completely + return drop_prev(special); } } } -void -TPushParser::do_gdelete_groupId() +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()); @@ -1028,28 +1257,43 @@ TPushParser::do_gdelete_groupId() if (prev) { // the cursor has a preceding element, so we try to remove it - gdelete_prev(); - - // We control if the group has to be removed, because the cursor - // might be the only element of the group. - if ((parent.first() == cursor) && parent.isG() && parent.hasId()) - rgreplace_father(); + 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. - rgreplace_father(); - - // we have to re-start the process, because it' a graphical delete - do_gdelete(); + 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_gdelete_groupId() +} // end of method do_drop_groupId() -void -TPushParser::do_gdelete_phantom_group() +std::string +TPushParser::do_drop_phantom_group(bool special) { // if we are here, the cursor MUST be a child of a // phantom group. @@ -1061,34 +1305,75 @@ TPushParser::do_gdelete_phantom_group() 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 + { + // 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--; + } + } + } + // the cursor has a preceding element, so we try to remove it - gdelete_prev(); + std::string str = drop_prev(special); - if (parent.size() == 1 && parent.parent().isSp()) + if (special) return str; + else { - // in this case the gdelete_prev has removed the only element preceding the cursor. - // If the phantom group is an sp's child, it means that the user has removed all \' in the - // phantom group. - // We can remove the phamtom group and the sp element. But we also can only remove the - // phantom group, giving the user the possibility of inserting an exponent. - // At the moment, we remove the sp element (and the phantom group), because it may be the correct - // behavior of a graphical deleting. - cursor.remove(); - parent.replace(cursor); - // now we have an sp element with two children: the first child (we don't know anything about it) - // and the cursror. - assert(cursor.parent().size() == 2); + // 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); - // to delete the script we can invoke the do_gdelete_script(), which will do all controls we need. - // To give the possibility of insetring an exponent, just remove the following istruction. - do_gdelete_script(); - } - else if (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 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 @@ -1100,7 +1385,8 @@ TPushParser::do_gdelete_phantom_group() if (!gfather) { // If here, the TML tree is in an inconsistent state - logger.error("do_gdelete_phantom: TML tree in a inconsistent state"); + logger.error("TML tree in a inconsistent state"); + return ""; } else if (gfather.isC()) { @@ -1108,6 +1394,9 @@ TPushParser::do_gdelete_phantom_group() // We have to control the nature of this MACRO. assert(!frames.empty()); Frame& frame = frames.top(); + + // this variable is useful in a special deletion + std::string macro_name = gfather.nameC(); if (frame.entry.leftOpen && frame.entry.rightOpen) { @@ -1138,6 +1427,13 @@ TPushParser::do_gdelete_phantom_group() // now we have the situation preceding the insertion of the leftOpen and rightOpen MACRO. // this MACRO no longer exists. 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 if (frame.entry.rightOpen) { @@ -1152,16 +1448,25 @@ TPushParser::do_gdelete_phantom_group() // 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 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()) { // the MACRO accepts arguments, and the phantom group in which - // the cursor is, rappresents a delimited argument + // 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(); @@ -1175,6 +1480,14 @@ TPushParser::do_gdelete_phantom_group() 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 { @@ -1182,59 +1495,69 @@ TPushParser::do_gdelete_phantom_group() // to control if the uncle is a delimited argument or not. if (uncle.isG() && !uncle.hasId()) { - // 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; + // 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(); // i update the parent (it should be the MACRO) + 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) - gdelete_prev(); + 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.warning("parser: TML tree in a strange state, but we try to recover from it"); - if (gfather.size() == 1) - { - cursor.remove(); - parent.remove(); - gfather.replace(cursor); - } - else - { - logger.warning("parser: TML tree in a very strange state, but we try to recover from it"); - cursor.remove(); - gfather.replace(cursor); - } + logger.error("TML tree in a strange state"); + return ""; } } // end of if (gfather.isC()) else if (gfather.is("cell")) { // A table is a control sequence, so there is a frame in the stack assert(!frames.empty()); - assert(frames.top().pos == 1); + 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()); + + // this variable is useful to handle the special deletion + std::string table_name = gfather.parent().parent().nameC(); + TNode row = gfather.parent(); // in this case the cell has no element, so the user wants to delete this cell. @@ -1246,7 +1569,7 @@ TPushParser::do_gdelete_phantom_group() if (!prev_cell) { - // in this case, the cell is the only cell in the row. + // 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(); @@ -1254,13 +1577,24 @@ TPushParser::do_gdelete_phantom_group() if (!prev_row) { - // 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); + 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 { @@ -1272,6 +1606,10 @@ TPushParser::do_gdelete_phantom_group() 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 @@ -1281,51 +1619,50 @@ TPushParser::do_gdelete_phantom_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()) { - // in this case, the user typed a \'. So this phantom group - // contained a sequence of \'. - // Maybe in this part will never be used, because, if we delete last \' in the - // phantom group, we remove the phantom group also - // - // In any case, if we are here we have two possibilities: - // we can delete the phantom group; - // we can delete the superscript. - // At the moment we implement the first solution. To implement the second one, just remove - // the line code after the logger.warning and remove comments from the remaining lines - logger.warning("parser: TML tree in a strange state, we try to recover from it"); - parent.replace(cursor); - - //cursor.remove(); - //parent.remove(); - //gfather.replace(cursor); + // 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 can decide to remove the math mode. - logger.info("Parser: nothing to delete"); + // but we could also decide to remove the math mode. + logger.warning("nothing to delete"); + return ""; } else { // cursor's grand father is undefined - logger.error("parser: TML tree is in a unknown state"); + logger.error("TML tree is in an unknown state"); + return ""; } } // end of the else of the if (prev) } -void -TPushParser::do_gdelete() +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 { @@ -1337,51 +1674,53 @@ TPushParser::do_gdelete() // 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()) { - do_gdelete_groupId(); + return do_drop_groupId(special); } else { - do_gdelete_phantom_group(); + return do_drop_phantom_group(special); } } // end of parent is group else if (parent.isC()) { - do_gdelete_macro(); + return do_drop_macro(special); } // end of parent is a MACRO else if (parent.isSp() || parent.isSb()) { - do_gdelete_script(); + 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_gdelete +} // 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::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); } } @@ -1420,23 +1759,7 @@ TPushParser::push(const TToken& token) // this delimiter is the last one for the argumet, // so the argument is completed cursor.remove(); - frame.pos++; - - if (frame.pos == frame.entry.pattern.size()) - { - // This token has completed the entry - advance(parent); - } - else if (frame.entry.paramDelimited(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); - } - else - parent.parent().append(cursor); + advance(parent); } } else @@ -1451,7 +1774,7 @@ TPushParser::push(const TToken& token) // - 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.error("parser: it's not the correct delimiter...you have to type " + frame.entry.pattern[frame.pos + 1].value); + logger.warning("it's not the correct delimiter...you have to type '" + frame.entry.pattern[frame.pos + 1].value + "'"); } else { @@ -1482,16 +1805,7 @@ TPushParser::push(const TToken& token) { // 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) { @@ -1507,14 +1821,16 @@ 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? - logger.debug("parser: token ignored: " + token.value); + logger.warning("ignored token: " + token.value); } - } else process(token); @@ -1527,14 +1843,13 @@ TPushParser::push(const TToken& token) } std::string -TPushParser::drop() +TPushParser::drop(bool special) { - do_gdelete(); + std::string str = do_drop(special); if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc); - return ""; + return str; } - void TPushParser::advance(const TNode& node) { @@ -1554,8 +1869,9 @@ TPushParser::advance(const TNode& node) else if (node.parent().isC()) { assert(!frames.empty()); - if (frames.top().pos == frames.top().entry.pattern.size()) + 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 @@ -1568,124 +1884,59 @@ TPushParser::advance(const TNode& node) advance(node.parent()); } } - else if (frames.top().entry.paramDelimited(frames.top().pos)) + 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 advance(node.parent()); } - -/* - * This version handles the case in which we have to - * create a delimited argument - void -TPushParser::advance(const TNode& node) +TPushParser::setCursorHint(const std::string& c) { - assert(node); - TNode parent = node.parent(); - if (!parent) - ; // nothing to do, the cursor is not in the document any more - else if (parent.isG()) - { - TNode next = node.next(); - if (next) next.insert(cursor); - else parent.append(cursor); - } - else if (parent.isC()) + if (cursor["val"] != c) { - if (node.next()) - ; // cursor removed - else - { - Frame& frame = frames.top(); - if (frame.pos == frame.entry.pattern.size()) - { - frames.pop(); - advance(parent); - } - else if (frame.entry.paramDelimited(frame.pos)) - { - // the next argument is delimited, so we have to create a phantom group - // with the cursor inside. We have to remember that, since we are here, - // the cursor has been removed - TNode g = doc.createG(); - g.append(cursor); - parent.append(g); - } - else - { - // otherwise, the next MACRO's argument is not delimited, so we just - // append the cursor to the MACRO - parent.append(cursor); - } - } + cursor["val"] = c; + if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc); } - else if (parent.is("math")) - ; // we are done - else - advance(parent); } -*/ -/* - * original advance -void -TPushParser::advance(const TNode& node) +bool +TPushParser::hideCursor() { - 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 (hiddenCursor++ == 0) { - TNode next = node.next(); - if (next) next.insert(cursor); - else parent.append(cursor); - } - else if (parent.isC()) - { - if (node.next()) - ; // cursor removed - else - { - Frame& frame = frames.top(); - if (frame.pos == frame.entry.pattern.size()) - { - frames.pop(); - advance(parent); - } - else - parent.append(cursor); - } + cursor["visible"] = "0"; + if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc); + return true; } - else if (parent.is("math")) - ; // we are done else - advance(parent); + return false; } -*/ - -void -TPushParser::setCursorHint(const std::string& c) +bool +TPushParser::showCursor() { - if (cursor["val"] != c) + if (hiddenCursor > 0 && --hiddenCursor == 0) { - cursor["val"] = c; + cursor["visible"] = "1"; if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc); + return true; } + else + return false; } bool