From a407cfb9bc31c5f9b94eb437d642e7582b641fc4 Mon Sep 17 00:00:00 2001 From: Paolo Marinelli Date: Mon, 24 Feb 2003 11:07:47 +0000 Subject: [PATCH] Added the special deletion. Pressing backspace, the user has a normal deletion (which can be either graphical or textual). Pressing backspace + alt, the user has a special deletion (which can be either textual or graphical). To implement this feature, all parser's methods concerning the deletion have been modified: now, all of them have a boolean parameter, which indicates which kind of deletion the method has to operate. All methods' names have been changed: the word gdelete has been substitued with drop. --- helm/DEVEL/mathml_editor/src/APushLexer.hh | 2 +- helm/DEVEL/mathml_editor/src/APushParser.hh | 2 +- helm/DEVEL/mathml_editor/src/LPushLexer.cc | 148 ++- helm/DEVEL/mathml_editor/src/LPushLexer.hh | 6 +- helm/DEVEL/mathml_editor/src/TPushLexer.cc | 2 +- helm/DEVEL/mathml_editor/src/TPushLexer.hh | 2 +- helm/DEVEL/mathml_editor/src/TPushParser.cc | 1200 +++++++++++-------- helm/DEVEL/mathml_editor/src/TPushParser.hh | 62 +- helm/DEVEL/mathml_editor/src/TTokenizer.cc | 2 +- helm/DEVEL/mathml_editor/src/TTokenizer.hh | 2 +- 10 files changed, 847 insertions(+), 581 deletions(-) diff --git a/helm/DEVEL/mathml_editor/src/APushLexer.hh b/helm/DEVEL/mathml_editor/src/APushLexer.hh index 342d18fe9..263da199d 100644 --- a/helm/DEVEL/mathml_editor/src/APushLexer.hh +++ b/helm/DEVEL/mathml_editor/src/APushLexer.hh @@ -9,7 +9,7 @@ public: virtual ~APushLexer() { }; virtual void push(char) = 0; - virtual void drop(bool = false) = 0; + virtual void drop(bool) = 0; virtual void reset(void) = 0; virtual bool error(void) const = 0; diff --git a/helm/DEVEL/mathml_editor/src/APushParser.hh b/helm/DEVEL/mathml_editor/src/APushParser.hh index 5344b7272..aa0d10a43 100644 --- a/helm/DEVEL/mathml_editor/src/APushParser.hh +++ b/helm/DEVEL/mathml_editor/src/APushParser.hh @@ -35,7 +35,7 @@ public: virtual void reset(void) = 0; virtual void push(const class TToken&) = 0; - virtual std::string drop(void) = 0; + virtual std::string drop(bool) = 0; virtual void setCursorHint(const std::string&) = 0; virtual bool hideCursor(void) = 0; virtual bool showCursor(void) = 0; diff --git a/helm/DEVEL/mathml_editor/src/LPushLexer.cc b/helm/DEVEL/mathml_editor/src/LPushLexer.cc index 6ee43b3e9..a41b016dd 100644 --- a/helm/DEVEL/mathml_editor/src/LPushLexer.cc +++ b/helm/DEVEL/mathml_editor/src/LPushLexer.cc @@ -38,11 +38,7 @@ LPushLexer::transaction(char ch, State newState) case ' ': parser.push(TToken(TToken::SPACE, ch)); break; case '~': parser.push(TToken(TToken::ACTIVE, ch)); break; case '%': parser.push(TToken(TToken::COMMENT)); break; - default: - if (isalpha(ch)) parser.push(TToken(TToken::LETTER, ch)); - else if (isdigit(ch)) parser.push(TToken(TToken::DIGIT, ch)); - else parser.push(TToken(TToken::OTHER, ch)); - break; + default: parser.push(TToken(TToken::OTHER, ch)); break; } state = newState; } @@ -59,7 +55,17 @@ LPushLexer::push(char ch) else if (isalpha(ch)) { buffer.push_back(ch); - state = LONG_IDENTIFIER; + state = IDENTIFIER; + } + else if (isspace(ch)) + { + // we translate this space in a macro. + parser.push(TToken(TToken::CONTROL, "space")); + } + else if (isdigit(ch)) + { + buffer.push_back(ch); + state = NUMBER; } else transaction(ch, ACCEPT); break; @@ -70,6 +76,12 @@ LPushLexer::push(char ch) state = MACRO; } else if (ch == -1) error(); + else if (isdigit(ch)) + { + // in this case, the previous '\' is ignored + buffer.push_back(ch); + state = NUMBER; + } else { parser.push(TToken(TToken::CONTROL, ch)); @@ -97,25 +109,30 @@ LPushLexer::push(char ch) buffer.erase(); state = ACCEPT; } - else - { + else if (isspace(ch)) + { parser.push(TToken(TToken::CONTROL, buffer)); buffer.erase(); - if (isspace(ch)) state = IGNORE_SPACE; - else transaction(ch, ACCEPT); + /* + * we comment this line, because a space after a macro + * is useful to exit from a macro + //parser.push(TToken(TToken::CONTROL, ";")); + */ + state = ACCEPT; } - break; - case IGNORE_SPACE: - if (ch == '\\') state = ESCAPE; - else if (ch == '#') state = PARAMETER; - else if (isspace(ch)) ; - else if (ch == -1) state = ACCEPT; - else if (isalpha(ch)) + else if (isdigit(ch)) { + parser.push(TToken(TToken::CONTROL, buffer)); + buffer.erase(); buffer.push_back(ch); - state = LONG_IDENTIFIER; + state = NUMBER; } - else transaction(ch, ACCEPT); + else + { + parser.push(TToken(TToken::CONTROL, buffer)); + buffer.erase(); + transaction(ch, ACCEPT); + } break; case PARAMETER: if (ch == -1) error(); @@ -125,7 +142,7 @@ LPushLexer::push(char ch) state = ACCEPT; } break; - case LONG_IDENTIFIER: + case IDENTIFIER: if (ch == -1) { parser.push(TToken(TToken::LETTER, buffer)); @@ -139,17 +156,9 @@ LPushLexer::push(char ch) else if (isspace(ch)) { parser.push(TToken(TToken::LETTER, buffer)); - - // the parser ignores spaces. But in this case, the space - // is a significant. So, we transform this space in the MACRO - // \; which will not be ignored. - // This is not a good solution. Having a special token, that will be - // interpreted as "function application" is a better one. - buffer.erase(); - buffer = ";"; - parser.push(TToken(TToken::CONTROL, buffer)); buffer.erase(); - state = IGNORE_SPACE; + parser.push(TToken(TToken::CONTROL, "space")); + state = ACCEPT; } else if (ch == '\\') { @@ -157,6 +166,12 @@ LPushLexer::push(char ch) buffer.erase(); state = ESCAPE; } + else if (ch == '#') + { + parser.push(TToken(TToken::LETTER, buffer)); + buffer.erase(); + state = PARAMETER; + } else { parser.push(TToken(TToken::LETTER, buffer)); @@ -164,6 +179,47 @@ LPushLexer::push(char ch) transaction(ch, ACCEPT); } break; + case NUMBER: + if (isdigit(ch)) buffer.push_back(ch); + else if (isspace(ch)) + { + parser.push(TToken(TToken::DIGIT, buffer)); + buffer.erase(); + parser.push(TToken(TToken::CONTROL, "space")); + state = ACCEPT; + } + else if (isalpha(ch)) + { + parser.push(TToken(TToken::DIGIT, buffer)); + buffer.erase(); + buffer.push_back(ch); + state = IDENTIFIER; + } + else if (ch == -1) + { + parser.push(TToken(TToken::DIGIT, buffer)); + buffer.erase(); + state = ACCEPT; + } + else if (ch == '\\') + { + parser.push(TToken(TToken::DIGIT, buffer)); + buffer.erase(); + state = ESCAPE; + } + else if (ch == '#') + { + parser.push(TToken(TToken::DIGIT, buffer)); + buffer.erase(); + state = PARAMETER; + } + else + { + parser.push(TToken(TToken::DIGIT, buffer)); + buffer.erase(); + transaction(ch, ACCEPT); + } + break; default: assert(0); break; @@ -174,7 +230,8 @@ LPushLexer::push(char ch) case ESCAPE: parser.setCursorHint("\\"); break; case MACRO: parser.setCursorHint("\\" + buffer); break; case PARAMETER: parser.setCursorHint("#"); break; - case LONG_IDENTIFIER: parser.setCursorHint(buffer); break; + case IDENTIFIER: parser.setCursorHint(buffer); break; + case NUMBER: parser.setCursorHint(buffer); break; default: parser.setCursorHint(""); break; } } @@ -187,13 +244,23 @@ LPushLexer::drop(bool alt) switch (state) { case ACCEPT: - case IGNORE_SPACE: - restore = parser.drop(); + restore = parser.drop(alt); if (restore.length() > 0 && restore[0] == '\\') { + cout << restore << endl; buffer = std::string(restore, 1, restore.length() - 1); state = (buffer.length() > 0) ? MACRO : ESCAPE; } + else if (restore.length() > 0 && isdigit(restore[0])) + { + buffer = restore; + state = NUMBER; + } + else if (restore.length() > 0 && isalpha(restore[0])) + { + buffer = restore; + state = IDENTIFIER; + } break; case ESCAPE: state = ACCEPT; @@ -203,13 +270,20 @@ LPushLexer::drop(bool alt) else buffer.erase(buffer.length() - 1, 1); if (buffer.length() == 0) state = ESCAPE; break; - case LONG_IDENTIFIER: - buffer.erase(buffer.length() - 1, 1); + case IDENTIFIER: + if (alt) buffer.erase(); + else buffer.erase(buffer.length() - 1, 1); + if (buffer.length() == 0) state = ACCEPT; + break; + case NUMBER: + if (alt) buffer.erase(); + else buffer.erase(buffer.length() - 1, 1); if (buffer.length() == 0) state = ACCEPT; break; case PARAMETER: default: - assert(0); + //assert(0); + error(); break; } @@ -218,6 +292,8 @@ LPushLexer::drop(bool alt) case ESCAPE: parser.setCursorHint("\\"); break; case MACRO: parser.setCursorHint("\\" + buffer); break; case PARAMETER: parser.setCursorHint("#"); break; + case IDENTIFIER: parser.setCursorHint(buffer); break; + case NUMBER: parser.setCursorHint(buffer); break; default: parser.setCursorHint(""); break; } } diff --git a/helm/DEVEL/mathml_editor/src/LPushLexer.hh b/helm/DEVEL/mathml_editor/src/LPushLexer.hh index 03ba29c02..db6dfb1bc 100644 --- a/helm/DEVEL/mathml_editor/src/LPushLexer.hh +++ b/helm/DEVEL/mathml_editor/src/LPushLexer.hh @@ -13,7 +13,7 @@ public: virtual ~LPushLexer() { }; virtual void push(char); - virtual void drop(bool = false); + virtual void drop(bool); virtual void reset(void); virtual void flush(void); virtual bool error(void) const; @@ -24,9 +24,9 @@ private: ACCEPT, ESCAPE, MACRO, - IGNORE_SPACE, PARAMETER, - LONG_IDENTIFIER + IDENTIFIER, + NUMBER }; void transaction(char, State); diff --git a/helm/DEVEL/mathml_editor/src/TPushLexer.cc b/helm/DEVEL/mathml_editor/src/TPushLexer.cc index c8d04f1bd..73ff25c61 100644 --- a/helm/DEVEL/mathml_editor/src/TPushLexer.cc +++ b/helm/DEVEL/mathml_editor/src/TPushLexer.cc @@ -138,7 +138,7 @@ TPushLexer::drop(bool alt) { case ACCEPT: case IGNORE_SPACE: - restore = parser.drop(); + restore = parser.drop(alt); if (restore.length() > 0 && restore[0] == '\\') { buffer = std::string(restore, 1, restore.length() - 1); diff --git a/helm/DEVEL/mathml_editor/src/TPushLexer.hh b/helm/DEVEL/mathml_editor/src/TPushLexer.hh index f2f409158..c8a99edde 100644 --- a/helm/DEVEL/mathml_editor/src/TPushLexer.hh +++ b/helm/DEVEL/mathml_editor/src/TPushLexer.hh @@ -13,7 +13,7 @@ public: virtual ~TPushLexer() { }; virtual void push(char); - virtual void drop(bool = false); + virtual void drop(bool); virtual void reset(void); virtual void flush(void); virtual bool error(void) const; diff --git a/helm/DEVEL/mathml_editor/src/TPushParser.cc b/helm/DEVEL/mathml_editor/src/TPushParser.cc index 63206ea7d..ad439908c 100644 --- a/helm/DEVEL/mathml_editor/src/TPushParser.cc +++ b/helm/DEVEL/mathml_editor/src/TPushParser.cc @@ -44,7 +44,7 @@ TPushParser::PRIME() const else return "?"; } -void +bool TPushParser::do_begin() { TNode parent = cursor.parent(); @@ -64,6 +64,7 @@ TPushParser::do_begin() cursor.replace(g); g.append(cursor); } + return true; } bool @@ -125,7 +126,7 @@ TPushParser::correctBrace() return ok; } -void +bool TPushParser::do_end() { TNode parent = cursor.parent(); @@ -134,6 +135,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")) { @@ -143,6 +145,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")) { @@ -167,24 +170,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("closing brace ignored"); + 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 @@ -192,52 +198,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(); @@ -249,6 +215,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")) { @@ -260,12 +227,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") @@ -273,23 +242,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("parser: math shift"); + return false; } } -void +bool TPushParser::do_align() { TNode parent = cursor.parent(); @@ -302,6 +275,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")) { @@ -316,26 +290,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("token ignored"); + return false; } -void +bool TPushParser::do_parameter(const std::string& p) { - // ??? + logger.warning("token ignored"); + return false; } -void +bool TPushParser::do_subscript() { TNode parent = cursor.parent(); @@ -349,6 +328,7 @@ TPushParser::do_subscript() cursor.replace(elem); elem.append(g); elem.append(cursor); + return true; } else { @@ -356,16 +336,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("token ignored"); + return false; } } -void +bool TPushParser::do_superscript() { TNode parent = cursor.parent(); @@ -379,6 +373,7 @@ TPushParser::do_superscript() cursor.replace(elem); elem.append(g); elem.append(cursor); + return true; } else { @@ -386,46 +381,52 @@ 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("token ignored: already over"); + return false; + } + else + { + parent["over"] = "1"; + return true; + } + } + else + { + logger.warning("token ignored"); + return false; } } -void +bool TPushParser::do_space(const std::string&) { - logger.debug("do_space"); + return false; } -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 @@ -435,7 +436,7 @@ TPushParser::isPrimes(const TNode& node) const return node.isG() && node.last() && node.last().is("o") && node.last()["val"] == PRIME(); } -void +bool TPushParser::do_apostrophe() { if (cursor.parent() && cursor.parent().isG()) @@ -443,11 +444,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(doc.createO(PRIME(), nextId++)); + 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(doc.createO(PRIME(), nextId++)); + return true; + } else { TNode elem = doc.create("sp"); @@ -456,51 +463,58 @@ TPushParser::do_apostrophe() elem.append(prev); elem.append(g); g.append(doc.createO(PRIME(), nextId++)); + return true; } } else { // is it an error? - logger.error("parser: you have to type one identifier before the ''"); + logger.warning("parser: you have to type an identifier before ''"); + return false; } } else { - // error ?? + logger.warning("token ignored: you have 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("token ignorde"); + return false; } -void +bool TPushParser::do_comment() { // ??? + return false; } -void +bool TPushParser::do_cr() { TNode parent = cursor.parent(); @@ -520,13 +534,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("token ignored: 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(); @@ -539,6 +560,7 @@ TPushParser::do_control(const std::string& name) t["name"] = name; cursor.replace(t); advance(t); + return true; } break; case TDictionary::OPERATOR: @@ -547,6 +569,7 @@ TPushParser::do_control(const std::string& name) t["name"] = name; cursor.replace(t); advance(t); + return true; } break; case TDictionary::NUMBER: @@ -555,50 +578,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) - { - 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) + if (parent.isG()) { - 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(); @@ -610,63 +632,97 @@ 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("token ignored: 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("parser: 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("token ignored"); + 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")); + + 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) && !special) { - 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); + utf8val = utf8val.erase(utf8val.length() - 1, 1); + return std::string(utf8val); + } + else + { + // in this case, the content of val could be in unicode, + // but we have attribute name, which doesn't contain character not representable + // with a byte. + return "\\" + utf8name.erase(utf8name.length() - 1, 1); + } } - + else if ((ucs4val.length() <= 1) && macro && special) return "\\" + utf8name.erase(utf8name.length() - 1, 1); + 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()); @@ -675,38 +731,72 @@ 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 the a sequence of '. + // Hence, we force a normal deleting, because the behaviour must be the same + // for the two kind of deleting + 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()) + { + 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 + // We can assume that the user wants to completely delete the undefined macro, + // but we can also handle this case as we handle tokens. At the moment, we delete the + // whole macro 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); + return ""; } else { @@ -730,19 +820,22 @@ 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 + // We could 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. + // At the moment, to give a standard behaviour, we remove the last element. assert(prev.first()); assert(prev.first().isG()); assert(prev.first() == prev.last()); @@ -754,7 +847,9 @@ TPushParser::gdelete_prev_macro() // TNode::replace. g.remove(); prev.replace(g.first(), TNode()); - //gdelete_prev(); + parent.append(cursor); + if (special) return "\\" + macro_name.erase(macro_name.length() - 1, 1); + else return do_drop(special); } else { @@ -762,6 +857,14 @@ TPushParser::gdelete_prev_macro() cursor.remove(); g.remove(); prev.replace(cursor); + if (special) return "\\" + macro_name.erase(macro_name.length() - 1, 1); + 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()) @@ -775,17 +878,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.erase(last_del.length() - 1, 1); + } + 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 behaviour to the graphical deleting, 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 { @@ -794,33 +917,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 deleting, 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()) @@ -832,13 +958,28 @@ 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.erase(macro_name.length() - 1, 1); + else return ""; + + // now we could start to remove something else. This behaviour would be justified by the + // fact that, generally, an empty MACRO gives no visual information about it. + // To adopt this behaviour, 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. @@ -849,30 +990,31 @@ TPushParser::gdelete_prev() if (prev.is("i") || prev.is("o") || prev.is("n")) { - 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 @@ -890,21 +1032,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 @@ -924,35 +1055,70 @@ 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); + + /* + * * the following istructions have sense, only if we remove the preceding one. + * + // 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(); + */ + } } 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 behaviour, 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 @@ -961,6 +1127,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 deleting + std::string macro_name = parent.nameC(); assert(!frames.empty()); Frame& frame = frames.top(); @@ -973,25 +1142,36 @@ 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.erase(macro_name.length() - 1, 1); + 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 hadn't no children and this is a graphical deleting, we try + // to remove something else + return do_drop(special); + + /* + * the following block of code has sense only if we remove the preceding instruction + * + // 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()) + { + // 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--; + } + */ } } else @@ -1006,25 +1186,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.erase(last_del.length() - 1, 1); + } + 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 deleting, 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 + // We "try", because the prev might be something that // a simple delete cannot remove completely - gdelete_prev(); + 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()); @@ -1036,28 +1233,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 deleting + 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. @@ -1069,34 +1281,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.erase(del.length() - 1, 1); + } + } + 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 @@ -1108,7 +1361,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()) { @@ -1116,6 +1370,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 deleting + std::string macro_name = gfather.nameC(); if (frame.entry.leftOpen && frame.entry.rightOpen) { @@ -1146,6 +1403,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.erase(macro_name.length() - 1, 1); + else + { + // to give a standard behaviour to the graphical deleting, we call the do_drop. + return do_drop(special); + } } else if (frame.entry.rightOpen) { @@ -1160,16 +1424,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.erase(macro_name.length() - 1, 1); + else + { + // to give a standard behaviour to the graphical deleting, 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(); @@ -1183,6 +1456,14 @@ TPushParser::do_gdelete_phantom_group() parent.remove(); gfather.replace(cursor); frames.pop(); + + if (special) return "\\" + macro_name.erase(macro_name.length() - 1, 1); + else + { + // once we have replaced the empty macro with the cursor, we can remove + // something else + return do_drop(special); + } } else { @@ -1190,59 +1471,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.erase(last_del.length() - 1, 1); + } + 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 deleting + 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. @@ -1254,7 +1545,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(); @@ -1262,13 +1553,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 { @@ -1280,6 +1582,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 deleting + // is equivalent to a normal deleting + return ""; } } // end of if (!prev_cell) else @@ -1289,51 +1595,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("Parser: nothing to delete"); + return ""; } else { // cursor's grand father is undefined logger.error("parser: TML tree is in a 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 { @@ -1345,51 +1650,52 @@ 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(); break; + case TToken::END: return do_end(); break; + case TToken::SHIFT: return do_shift(); break; + case TToken::ALIGN: return do_align(); break; + case TToken::EOL: return do_eol(); break; + case TToken::PARAMETER: return do_parameter(token.value); break; + case TToken::SUPERSCRIPT: return do_superscript(); break; + case TToken::SUBSCRIPT: return do_subscript(); break; + case TToken::SPACE: return do_space(token.value); break; + case TToken::LETTER: return do_letter(token.value); break; + case TToken::DIGIT: return do_digit(token.value); break; + case TToken::OTHER: return do_other(token.value); break; + case TToken::ACTIVE: return do_active(token.value); break; + case TToken::COMMENT: return do_comment(); break; + case TToken::CONTROL: return do_control(token.value); break; } } @@ -1428,23 +1734,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 @@ -1459,7 +1749,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("parser: it's not the correct delimiter...you have to type " + frame.entry.pattern[frame.pos + 1].value); } else { @@ -1490,16 +1780,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) process(token); } else if (frame.entry.pattern[frame.pos] == token) { @@ -1515,14 +1796,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("parser: token ignored: " + token.value); } - } else process(token); @@ -1535,14 +1818,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) { @@ -1562,8 +1844,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 @@ -1576,116 +1859,25 @@ 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) -{ - 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 (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); - } - } - } - else if (parent.is("math")) - ; // we are done - else - advance(parent); -} -*/ - -/* - * original advance -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()) - { - 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); - } - } - else if (parent.is("math")) - ; // we are done - else - advance(parent); -} -*/ - - void TPushParser::setCursorHint(const std::string& c) { diff --git a/helm/DEVEL/mathml_editor/src/TPushParser.hh b/helm/DEVEL/mathml_editor/src/TPushParser.hh index 06020b52f..04edacb63 100644 --- a/helm/DEVEL/mathml_editor/src/TPushParser.hh +++ b/helm/DEVEL/mathml_editor/src/TPushParser.hh @@ -19,7 +19,7 @@ public: virtual void reset(void); virtual void push(const TToken&); - virtual std::string drop(void); + virtual std::string drop(bool); virtual void setCursorHint(const std::string&); virtual bool hideCursor(void); virtual bool showCursor(void); @@ -36,43 +36,41 @@ private: - void do_begin(void); - void do_end(void); - void do_shift(void); - void do_align(void); - void do_eol(void); - void do_parameter(const std::string&); - void do_superscript(void); - void do_subscript(void); - void do_space(const std::string&); - void do_letter(const std::string&); - void do_digit(const std::string&); - void do_other(const std::string&); - void do_active(const std::string&); - void do_comment(void); - void do_control(const std::string&); + bool do_begin(void); + bool do_end(void); + bool do_shift(void); + bool do_align(void); + bool do_eol(void); + bool do_parameter(const std::string&); + bool do_superscript(void); + bool do_subscript(void); + bool do_space(const std::string&); + bool do_letter(const std::string&); + bool do_digit(const std::string&); + bool do_other(const std::string&); + bool do_active(const std::string&); + bool do_comment(void); + bool do_control(const std::string&); - void gdelete_prev_token(void); - void gdelete_prev_script(void); - void gdelete_prev_group(void); - void gdelete_prev_macro(void); - void gdelete_prev(void); - + std::string drop_prev_token(bool); + std::string drop_prev_script(bool); + std::string drop_prev_group(bool); + std::string drop_prev_macro(bool); + std::string drop_prev(bool); void rgreplace_father(void); - - void do_gdelete_script(void); - void do_gdelete_macro(void); - void do_gdelete_groupId(void); - void do_gdelete_phantom_group(void); - void do_gdelete(void); - - void do_cr(void); - void do_apostrophe(void); + std::string do_drop_script(bool); + std::string do_drop_macro(bool); + std::string do_drop_groupId(bool); + std::string do_drop_phantom_group(bool); + std::string do_drop(bool); + + bool do_cr(void); + bool do_apostrophe(void); void advance(const TNode&); bool correctBrace(void); - void process(const TToken&); + bool process(const TToken&); struct Frame { diff --git a/helm/DEVEL/mathml_editor/src/TTokenizer.cc b/helm/DEVEL/mathml_editor/src/TTokenizer.cc index a78b7cc5d..7a8736cee 100644 --- a/helm/DEVEL/mathml_editor/src/TTokenizer.cc +++ b/helm/DEVEL/mathml_editor/src/TTokenizer.cc @@ -37,7 +37,7 @@ TTokenizer::push(const TToken& token) } std::string -TTokenizer::drop() +TTokenizer::drop(bool alt) { assert(0); return ""; diff --git a/helm/DEVEL/mathml_editor/src/TTokenizer.hh b/helm/DEVEL/mathml_editor/src/TTokenizer.hh index da534fed7..00960207d 100644 --- a/helm/DEVEL/mathml_editor/src/TTokenizer.hh +++ b/helm/DEVEL/mathml_editor/src/TTokenizer.hh @@ -19,7 +19,7 @@ public: private: virtual void reset(void); virtual void push(const TToken&); - virtual std::string drop(void); + virtual std::string drop(bool = false); virtual void setCursorHint(const std::string&) { }; virtual bool hideCursor(void) { return false; }; virtual bool showCursor(void) { return false; }; -- 2.39.2