+++ /dev/null
-/* This file is part of EdiTeX, an editor of mathematical
- * expressions based on TeX syntax.
- *
- * Copyright (C) 2002-2003 Luca Padovani <lpadovan@cs.unibo.it>,
- * 2003 Paolo Marinelli <pmarinel@cs.unibo.it>.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * For more information, please visit the project's home page
- * http://helm.cs.unibo.it/editex/
- * or send an email to <lpadovan@cs.unibo.it>
- */
-
-#include <cassert>
-
-#include "ALogger.hh"
-#include "TPushParser.hh"
-#include "AMathMLFactory.hh"
-
-TPushParser::TPushParser(ALogger& l, const TDictionary& d) : APushParser(l), dictionary(d)
-{
- init();
-}
-
-TPushParser::TPushParser(ALogger& l, AMathMLFactory& f, const TDictionary& d) : APushParser(l, f), dictionary(d)
-{
- init();
-}
-
-TPushParser::~TPushParser()
-{
-}
-
-void
-TPushParser::init()
-{
- cursor = doc.create("cursor");
- cursor["visible"] = "1";
- hiddenCursor = 0;
- reset();
-}
-
-void
-TPushParser::reset()
-{
- nextId = 1;
- if (cursor.parent()) cursor.remove();
- cursor["val"] = "";
- doc.reset();
- doc.root().append(cursor);
- if (factory && !frozen()) factory->documentModified(doc);
-}
-
-TNode
-TPushParser::PRIME()
-{
- const TDictionary::Entry entry = dictionary.find("prime");
- 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;
- }
-}
-
-bool
-TPushParser::do_begin()
-{
- TNode parent = cursor.parent();
- if (parent.isC() && dictionary.find(parent.nameC()).table)
- {
- TNode row = doc.create("row");
- TNode cell = doc.create("cell");
- TNode g = doc.createG();
- row.append(cell);
- cell.append(g);
- g.append(cursor);
- parent.append(row);
- }
- else
- {
- TNode g = doc.createG(nextId++);
- cursor.replace(g);
- g.append(cursor);
- }
- return true;
-}
-
-bool
-TPushParser::correctBrace()
-{
- // this method MUST be invoked when the cursor is child of a
- // phantom group, which in turn is the last rightOpen MACRO's child.
- // The only way to exit from a rightOpen MACRO is opening a group before
- // inserting the MACRO and, once the MACRO is completely inserted, closing
- // the group.
- // This method return true if the condition above is true. False, otherwise.
- assert(cursor.parent() && cursor.parent().isG() && !cursor.parent().hasId());
- TNode parent = cursor.parent();
- assert(parent.parent() && parent.parent().isC());
- assert(!frames.empty());
- Frame& frame = frames.top();
- assert(frame.entry.rightOpen);
- assert(parent.parent().last() == parent);
-
- TNode c = parent.parent();
- bool stop = false;
- bool ok = false;
- TNode node = c.parent();
- do
- {
- if (node.isG() && node.hasId())
- {
- // in this case, the rightOpen MACRO is a child of a group with id.
- // So, the '}' is correct
- ok = true;
- stop = true;
- }
- else if (node.isG())
- {
- // the MACRO is a phantom group's child. We have to control why we
- // have this phantom group
- TNode nodeParent = node.parent();
- if (nodeParent && nodeParent.isC())
- {
- // we have to control the nature of this MACRO
- const TDictionary::Entry& entry = dictionary.find(nodeParent.nameC());
- if (entry.rightOpen && node == nodeParent.last())
- {
- // in this case we have to re-iterate the process
- node = nodeParent.parent();
- }
- else stop = true;
- }
- else stop = true;
- }
- else
- {
- // at the moment we assume that a MACRO cannot be child of an element other than a group
- stop = true;
- }
- }
- while (!stop);
-
- return ok;
-}
-
-bool
-TPushParser::do_end()
-{
- TNode parent = cursor.parent();
- if (parent && parent.isG() && parent.hasId())
- {
- // normal closing brace for an explicitly open group
- cursor.remove();
- advance(parent);
- return true;
- }
- else if (parent && parent.isG() && parent.parent() && parent.parent().is("cell"))
- {
- assert(!frames.empty());
- // closing brace for a structure in which & or \cr have been used
- TNode row = parent.parent().parent();
- assert(row && row.is("row"));
- assert(row.parent());
- advance(row);
- return true;
- }
- else if (parent && parent.isG() && !parent.hasId() && parent.parent() && !parent.parent().is("math"))
- {
- // In this case, we have to control the cursor's grand parent.
- TNode gparent = parent.parent();
-
- if (gparent.isC() && gparent.last() == parent)
- {
- // a frame MUST be in the stack
- assert(!frames.empty());
-
- // we have to control the nature of this macro
- if (frames.top().entry.rightOpen)
- {
- // in this case, the '}' character is the proper way to exit from the phantom group, and
- // in particular, this character means that the user wants to exit from the MACRO.
- // A rightOpen MACRO MUST be descendant of a group with Id. This '}' is the closing brace of this
- // group. So, we have to control if this group exists. This groyp could exist, but this MACRO could
- // be another MACRO's child, so we have to control this last MACRO recursively. This recurive control
- // is done by the correctBrace method.
- if (!correctBrace())
- {
- // the '}' is not correct
- logger.warning("nothing to close");
- return false;
- }
- else
- {
- cursor.remove();
- advance(parent);
- return true;
- }
- }
- else
- {
- logger.warning("ignored closing brace");
- return false;
- }
- }
- else
- {
- // at the moment, a phantom group with the cursor inside can be a MACRO's child or a cell's child, and these cases
- // are handled in other blocks of code.
- logger.error("do_end: strange TML tree");
- return false;
- }
- }
- else
- {
- // 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);
- }
-}
-
-bool
-TPushParser::do_shift()
-{
- TNode parent = cursor.parent();
- assert(parent);
- if (parent.is("tex"))
- {
- TNode math = doc.create("math", nextId++);
- TNode g = doc.createG();
- cursor.replace(math);
- math.append(g);
- g.append(cursor);
- return true;
- }
- else if (parent.isG() && !parent.hasId() && parent.parent() && parent.parent().is("math"))
- {
- if (cursor.prev())
- {
- // there is something before the cursor, hence this is the
- // closing math shift
- if (parent.parent()["display"] != "1")
- {
- // 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")
- {
- // 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.warning("not allowed here");
- return false;
- }
-}
-
-bool
-TPushParser::do_align()
-{
- TNode parent = cursor.parent();
- if (parent && parent.isG() && parent.hasId())
- {
- // alignment tab used for the first time inside a group
- TNode row = doc.create("row");
- TNode cell = doc.create("cell");
- TNode g = doc.createG();
- row.append(cell);
- cell.append(g);
- g.append(parent.first(), cursor);
- return true;
- }
- else if (parent && parent.isG() && parent.parent().is("cell"))
- {
- // alignment tab used within a cell
- TNode oldCell = parent.parent();
- assert(oldCell && oldCell.is("cell"));
- TNode row = oldCell.parent();
- assert(row && row.is("row"));
- TNode cell = doc.create("cell");
- if (oldCell.next()) oldCell.next().insert(cell);
- else row.append(cell);
- TNode g = doc.createG();
- cell.append(g);
- g.append(cursor);
- return true;
- }
- else
- {
- logger.warning("alignment tab used outside matrix");
- return false;
- }
-}
-
-bool
-TPushParser::do_eol()
-{
- //if (cursor.parent()) cursor.remove();
- logger.warning("ignored token");
- return false;
-}
-
-bool
-TPushParser::do_parameter(const std::string& p)
-{
- logger.warning("ignored token");
- return false;
-}
-
-bool
-TPushParser::do_subscript()
-{
- TNode parent = cursor.parent();
- if (parent.isG())
- {
- TNode prev = cursor.prev();
- if (!prev)
- {
- TNode elem = doc.create("sb", nextId++);
- TNode g = doc.createG();
- cursor.replace(elem);
- elem.append(g);
- elem.append(cursor);
- return true;
- }
- else
- {
- TNode elem = doc.create("sb", nextId++);
- prev.replace(elem);
- elem.append(prev);
- elem.append(cursor);
- return true;
- }
- }
- else if (parent.isSb() && cursor == parent[1])
- {
- if (parent["under"] == "1")
- {
- logger.warning("already under");
- return false;
- }
- else
- {
- parent["under"] = "1";
- return true;
- }
- }
- else
- {
- logger.warning("ignored token");
- return false;
- }
-}
-
-bool
-TPushParser::do_superscript()
-{
- TNode parent = cursor.parent();
- if (parent.isG())
- {
- TNode prev = cursor.prev();
- if (!prev)
- {
- TNode elem = doc.create("sp", nextId++);
- TNode g = doc.createG();
- cursor.replace(elem);
- elem.append(g);
- elem.append(cursor);
- return true;
- }
- else
- {
- TNode elem = doc.create("sp", nextId++);
- prev.replace(elem);
- elem.append(prev);
- elem.append(cursor);
- return true;
- }
- }
- else if (parent.isSp() && cursor == parent[1])
- {
- if (parent["over"] == "1")
- {
- logger.warning("already over");
- return false;
- }
- else
- {
- parent["over"] = "1";
- return true;
- }
- }
- else
- {
- logger.warning("ignored token");
- return false;
- }
-}
-
-bool
-TPushParser::do_ignorablespace(const std::string& s)
-{
- // At the moment, do nothing
-}
-
-bool
-TPushParser::do_space(const std::string&)
-{
- TNode elem = doc.createS(nextId++);
- cursor.replace(elem);
- advance(elem);
- return true;
-}
-
-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;
-}
-
-bool
-TPushParser::do_digit(const std::string& s)
-{
- 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()["name"] == "prime";
-}
-
-bool
-TPushParser::do_apostrophe()
-{
- if (cursor.parent() && cursor.parent().isG())
- {
- if (TNode prev = cursor.prev())
- {
- if (prev.isSp() && prev[1] && isPrimes(prev[1]))
- {
- 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(PRIME());
- return true;
- }
- else
- {
- TNode elem = doc.create("sp");
- TNode g = doc.createG();
- prev.replace(elem);
- elem.append(prev);
- elem.append(g);
- g.append(PRIME());
- return true;
- }
- }
- else
- {
- // is it an error?
- logger.warning("you have to insert an identifier before a ''");
- return false;
- }
- }
- else
- {
- logger.warning("cursor has to be in a group");
- return false;
- }
-}
-
-bool
-TPushParser::do_other(const std::string& s)
-{
- switch (s[0])
- {
- case '\'':
- return do_apostrophe();
- break;
- default:
- /*cout << "TPushParser::do_other " << s << endl;
- cout << "DOCUMENT: " << static_cast<GdomeNode*>(cursor.element().get_ownerDocument()) << endl;*/
- TNode elem = doc.createT("o", s, nextId++);
- cursor.replace(elem);
- advance(elem);
- return true;
- break;
- }
-}
-
-bool
-TPushParser::do_active(const std::string&)
-{
- // ??? space?
- logger.warning("ignored token");
- return false;
-}
-
-bool
-TPushParser::do_comment()
-{
- // ???
- return false;
-}
-
-bool
-TPushParser::do_cr()
-{
- TNode parent = cursor.parent();
- if (parent && parent.isG() &&
- parent.parent() && parent.parent().is("cell") &&
- parent.parent().parent() && parent.parent().parent().is("row"))
- {
- TNode oldRow = parent.parent().parent();
- assert(oldRow);
- TNode table = oldRow.parent();
- assert(table);
- TNode row = doc.create("row");
- TNode cell = doc.create("cell");
- TNode g = doc.createG();
- if (oldRow.next()) oldRow.next().insert(row);
- else table.append(row);
- 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;
- }
-}
-
-bool
-TPushParser::do_control(const std::string& name)
-{
- if (name == "cr") return do_cr();
- else
- {
- TNode parent = cursor.parent();
- const TDictionary::Entry& entry = dictionary.find(name);
- switch (entry.cls)
- {
- case TDictionary::IDENTIFIER:
- {
- TNode t = doc.createI(entry.value, nextId++);
- t["name"] = name;
- cursor.replace(t);
- advance(t);
- return true;
- }
- break;
- case TDictionary::OPERATOR:
- {
- TNode t = doc.createO(entry.value, nextId++);
- t["name"] = name;
- cursor.replace(t);
- advance(t);
- return true;
- }
- break;
- case TDictionary::NUMBER:
- {
- TNode t = doc.createN(entry.value, nextId++);
- t["name"] = name;
- cursor.replace(t);
- advance(t);
- return true;
- }
- break;
- case TDictionary::MACRO:
- {
- 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();
- m.append(g);
- g.append(cursor);
- }
- else
- m.append(cursor);
- }
- else
- {
- // 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;
- }
- }
- break;
- case TDictionary::UNDEFINED:
- {
- logger.warning("using undefined macro " + name);
- TNode m = doc.createC(name, nextId++);
- cursor.replace(m);
- advance(m);
- return true;
- }
- break;
- default:
- {
- //assert(0);
- logger.warning("ignored token");
- return false;
- }
- }
- }
-}
-
-std::string
-TPushParser::drop_prev_token(bool special)
-{
- assert(cursor.prev());
- assert(cursor.parent());
- TNode prev = cursor.prev();
- assert(prev.isT());
-
- DOM::UCS4String ucs4val = prev.element().getAttribute("val");
- bool macro = prev.element().hasAttribute("name");
- std::string utf8name;
- if (macro) utf8name = prev.element().getAttribute("name");
-
- cursor.remove();
- prev.replace(cursor);
-
- if (cursor.parent().isC())
- {
- // in this case we have removed an element of a MACRO.
- // we can assert that this element was a non delimited argument
- assert(!frames.empty());
- Frame& frame = frames.top();
- assert(frame.pos > 0);
- frame.pos--;
- }
-
- if ((ucs4val.length() > 1))
- {
- if (!macro)
- {
- // in this case we can return the content of ucs4val, but we have
- // to convert it in a utf8
- DOM::GdomeString gdsval(ucs4val);
- std::string utf8val(gdsval);
- switch (utf8val[utf8val.length() - 1])
- {
- 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 "";
-}
-
-std::string
-TPushParser::drop_prev_script(bool special)
-{
- // this method deletes an sp or an sb preceding the cursor
- assert(cursor.prev());
- assert(cursor.parent());
- TNode prev = cursor.prev();
- assert(prev.is("sp") || prev.is("sb"));
- cursor.remove();
- prev.append(cursor);
- // we can invoke the drop_prev, because a sp (sb) MUST have two children
- // but we cannot invoke do_drop_script because it assumes when called, the first
- // child has been removed yet.
- if (cursor.prev().isG() && !prev.hasId())
- {
- // in this case, the user has inserted a sequence of '.
- // Hence, we force a normal deletion, because the behavior must be the same
- // for the two kind of deletion
- return drop_prev(false);
- }
- else return drop_prev(special);
-}
-
-std::string
-TPushParser::drop_prev_group(bool special)
-{
- assert(cursor.prev() && cursor.prev().isG());
- TNode parent = cursor.parent();
- TNode prev = cursor.prev();
- cursor.remove();
- prev.append(cursor);
-
- if (parent.isC() && prev.hasId())
- {
- // 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);
- }
-}
-
-std::string
-TPushParser::drop_prev_macro(bool special)
-{
- assert(cursor.parent());
- assert(cursor.prev());
- TNode prev = cursor.prev();
- assert(prev.isC());
-
- std::string macro_name = prev.nameC();
-
- TNode parent = cursor.parent();
-
- const TDictionary::Entry& entry = dictionary.find(prev["name"]);
-
- if (!entry.defined())
- {
- // In this case, with a normal deletion, we completely remove the macro.
- // With a special deletion, we remove the last character of the macro's name.
- cursor.remove();
- prev.replace(cursor);
- if (cursor.parent().isC())
- {
- // we have removed a macro's child
- assert(!frames.empty());
- frames.top().pos--;
- }
- if (special) return "\\" + macro_name.erase(macro_name.length() - 1, 1); // we remove the last char, because an undefined macro's name is visible
- return "";
- }
- else
- {
- // we start to remove a MACRO. Different actions must be taken, based on the nature
- // of the MACRO. In some cases, we can't remove the MACRO immediately, in other
- // cases it's correct. In the first set of cases, we have to update the stack, pushing
- // a frame in it with a correct value of pos, in the
- // second one, we must not push a frame in the stack
-
- if (entry.rightOpen)
- {
- // In this fragment of code we also handle the leftOpen && rightOpen MACRO.
- // since the control element is rightOpen, the cursor should be placed after
- // the last child of the control element's last child, and than, we try to remove something.
- // A frame MUST be pushed in the stack, because we dont' know if the following actions
- // will completely remove the MACRO.
- frames.push(Frame(entry));
-
- // Since the MACRO is rightOpen, the last child of the MACRO must be a phantom group
- assert(prev.last().isG() && !prev.last().hasId());
-
- cursor.remove();
- prev.last().append(cursor);
-
- 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.
- // We could remove the last child of the phantom group, but
- // it's not clear if it's the correct behavior of the graphical deletion.
- // At the moment, to give a standard behavior, we remove the last element.
- // With a special deletion, we do not remove it.
- assert(prev.first());
- assert(prev.first().isG());
- assert(prev.first() == prev.last());
-
- TNode g = prev.first();
- if (g.size())
- {
- // in this case, the phantom group has at least one child, so we can call the
- // TNode::replace.
- g.remove();
- prev.replace(g.first(), TNode());
- parent.append(cursor);
- if (special) return "\\" + macro_name;
- else return do_drop(special);
- }
- else
- {
- // otherwise, the phantom group has no children, so we remove it, also the MACRO.
- cursor.remove();
- g.remove();
- prev.replace(cursor);
- if (special) return "\\" + macro_name;
- else
- {
- // Once removed this empty macro, we could try to remove something else.
- // This would be justified by the fact that, generally, an empty macro gives no visual information
- // about it.
- return do_drop(special); // special is false
- }
- }
- }
- else if (!entry.pattern.empty())
- {
- // we have to start to remove a MACRO which accepts arguments.
- // If the MACRO accepts arguments, the MACRO has at least one child
- assert(prev.size() >= 1);
-
- // Differnt actions must be taken, based on the nature of the last child
- // of the MACRO. We have to distinguish the case in which it's a delimited argument,
- // frome the one in which it's a not delimited argument.
- if (prev.last().isG() && !prev.last().hasId())
- {
- if (special)
- {
- // in this case, we have to start removing the last delimiter
- frames.push(Frame(entry, entry.pattern.size() - 2));
-
- cursor.remove();
- prev.last().append(cursor);
-
- std::string last_del = entry.pattern[entry.pattern.size() - 1].value;
-
- return "\\" + last_del;
- }
- else
- {
- // the last argument of the MACRO is a delimited argumet. We ideally remove
- // the sequence of delimiters
- cursor.remove();
- prev.last().append(cursor);
- // we have to push a frame with a correct value of pos
- assert(entry.previousParam(entry.pattern.size()) != entry.pattern.size());
-
- unsigned sequence_length = entry.pattern.size() - entry.previousParam(entry.pattern.size()) - 1;
- unsigned p = entry.pattern.size() - sequence_length - 1;
- // now, p is the correct value of pos, and we can push the frame.
- frames.push(Frame(entry, p));
-
- // To give a standard behavior to the graphical deletion, we remove the last
- // element of the macro. Since we are in a phantom group, we can invoke the
- // do_drop_phantom_group(special).
- return do_drop_phantom_group(special);
- }
- }
- else
- {
- // in this case, the last child of the MACRO is not a delimited argument, so we try
- // to remove it, but we have to take differnt actions if the MACRO is a table with rows or not.
- cursor.remove();
- if (entry.table == 1 && prev.last().is("row"))
- {
- // in this case the cursor has to be appended to the group associated to
- // the last cell of the last row of the table
- assert(prev.last().last().is("cell") && prev.last().last().first().isG());
- prev.last().last().first().append(cursor);
-
- // we have to push a frame in the stack. Since tables has a pattern size = 1, we have to
- // set pos at 0, because appending the cursor to the last cell means that this argument
- // is not whole inserted.
- // We don't call frames.push(Frame(entry)), because it incoditionaly set pos at 0. The
- // following line is more general
- frames.push(Frame(entry, entry.pattern.size() - 1));
- if (special)
- {
- // to type a table with rows and cells, the user had typed a
- // "{", and to exit from it, the user had inserted a "}".
- // Since we are in a special deletion, we just idealy remove the "}"
- return "";
- }
- else return do_drop_phantom_group(special);
- }
- else
- {
- // we push a frame in the stack with a correct value of member pos.
- // This correct value is the size of the pattern - 1, because we have been started to delete
- // a MACRO. It means that all of the MACRO's arguments have been inserted, but
- frames.push(Frame(entry, entry.pattern.size()));
- prev.append(cursor);
- return drop_prev(special);
- }
-
- } // end of the else of the if (prev.last().isG() && !prev.last().hasId())
-
- } // end of if (!entry.pattern.empty())
- else
- {
- // if we are here, the MACRO preceding the cursor, is !(rightOpen || leftOpen),
- // and has no pattern. It means that it has no children.
- // We can replace it with the cursor
- assert(prev.size() == 0);
- cursor.remove();
- prev.replace(cursor);
- if (cursor.parent().isC())
- {
- // we have removed an empty macro, which was a non delimited argument of a macro.
- // We have to decrement pos
- assert(!frames.empty());
- frames.top().pos--;
- }
-
- if (special) return "\\" + macro_name;
- else return "";
-
- // now we could start to remove something else. This behavior would be justified by the
- // fact that, generally, an empty MACRO gives no visual information about it.
- // To adopt this behavior, just remove the comment to the following instruction
- // return do_drop(special);
- }
- } // end of defined MACRO
-
-}
-
-std::string
-TPushParser::drop_prev(bool special)
-{
- // if in this function, the prev of cursor does exist, also the parent and we want a graphical deletion.
-
- assert(cursor.prev());
- assert(cursor.parent());
-
- TNode prev = cursor.prev();
-
- if (prev.isT())
- {
- return drop_prev_token(special);
- }
- else if (prev.isSp() || prev.isSb())
- {
- return drop_prev_script(special);
- }
- else if (prev.isG())
- {
- return drop_prev_group(special);
- }
- else if (prev.isC())
- {
- // here, we also treat the case in which the MACRO is a table
- return drop_prev_macro(special);
- }
- else
- {
- // not handled. Future cases...
- return "";
- }
-
-} // end of method
-
-void
-TPushParser::rgreplace_father()
-{
- // this method MUST only be invoked, when the cursor
- // is the only child of a group with id. This function
- // replaces the group with the cursor. But if the new parent
- // is a group with id and the cursor is the only child of the
- // group, the new parent is replaced...and so on.
- // r stands for recursive, g stands for graphical.
- assert(cursor.parent());
- assert(cursor.parent().isG() && cursor.parent().hasId());
-
- TNode parent = cursor.parent();
-
- while (parent.isG() && parent.hasId() && (parent.first() == cursor))
- {
- parent.replace(cursor);
- parent = cursor.parent();
- }
-}
-
-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
- // element preceding the cursor. The sp's (or sb's) parent
- // MUST NOT be a MACRO.
- // The element preceding the cursor is the base of the script.
-
- assert(cursor.parent() && (cursor.parent().isSp() || cursor.parent().isSb()));
- TNode parent = cursor.parent();
-
- assert(parent.size() == 2);
- assert(parent.parent() && !parent.parent().isC());
-
- TNode prev = cursor.prev();
- cursor.remove();
- if (prev.isG() /*&& !prev.hasId()*/ && (prev.size() == 0))
- {
- // in this case, the script's base is a group with no elements, so
- // we have to remove the entire MACRO, replacing it with the cursor.
- // This situation occurs when the user had typed something like this
- // $....{}^
- // or this
- // $^
- // or this
- // $...{^
- //
- if (special && prev.hasId())
- {
- // in this case, the user has typed: ...{}^
- // hence we idealy remove the ^
- parent.replace(prev);
- prev.parent().append(cursor);
- return "";
- }
- else if (!prev.hasId())
- {
- // we idealy remove the ^, but the phantom group
- // has to be removed, also
- prev.remove();
- parent.replace(cursor);
- return "";
- }
- else
- {
- prev.remove();
- parent.replace(cursor);
-
- // since the script had no children, we can try to remove something else.
- // Since we don't know who is cursor's parent, and who is cursor's preceding
- // element, we invoke the do_drop()
- return do_drop(special);
- }
- }
- else
- {
- // in this case, the prev has to replace the script.
- parent.replace(prev);
- prev.parent().append(cursor);
- // now prev have a preceding element
- assert(cursor.parent().size() > 1);
-
- if (special) return "";
- else
- {
- // to give a standard behavior, we try to remove the element, which was
- // the script's base.
- return do_drop(special);
- }
- }
-
-} // end of method do_drop_script
-
-std::string
-TPushParser::do_drop_macro(bool special)
-{
- // If we are here, the cursor is a child of a MACRO and this means
- // that there is an open frame for the control element
- // and this element is closed at either side (no leftOpen no rightOpen)
- // and the MACRO is waiting for a not delimited argument, so
- // we can assert that frame.entry.pattern.size() >= 1
- assert(cursor.parent() && cursor.parent().isC());
- TNode parent = cursor.parent();
-
- // this string is useful iff we have a special deletion.
- std::string macro_name = parent.nameC();
-
- assert(!frames.empty());
- Frame& frame = frames.top();
- assert(frame.entry.pattern.size() >= 1);
-
- // we have to take different actions, based on if a preceding element exists
- // or not
- TNode prev = cursor.prev();
- if (!prev)
- {
- // in this case, a prev does not exist, so the actions of deleting means
- // that we have to remove the MACRO. So we have to pop the stack.
- assert(frame.pos == 0);
-
- parent.replace(cursor);
- frames.pop();
-
- if (special) return "\\" + macro_name;
- else
- {
- // Since the macro had no children and this is a graphical deletion, we try
- // to remove something else
- return do_drop(special);
- }
- }
- else
- {
- // a prev does exist, we have to control if it's a delimited argument or not.
- if (prev.isG() && !prev.hasId())
- {
- // in this case, prev is a delimited argument, so we have
- // to ideally remove the sequence of delimiters
- Frame& frame = frames.top();
- assert(frame.pos > 1);
- cursor.remove();
- prev.append(cursor);
- assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
-
- if (special)
- {
- // in this case we have to start removing the last delimimeter.
- // It means that we return in a situation where the user has not entirely
- // inserted the delimited argument. So, we have to decrement frame.pos of
- // two units: the delimiter and the actual argument
- std::string last_del = frame.entry.pattern[frame.pos - 1].value;
- frame.pos = frame.pos - 2;
- return "\\" + last_del;
- }
- else
- {
- // 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 something that
- // a simple deletion cannot remove completely
- return drop_prev(special);
- }
- }
-
-}
-
-std::string
-TPushParser::do_drop_groupId(bool special)
-{
- // if we are here, the cursor's parent is a group with Id
- assert(cursor.parent() && cursor.parent().isG() && cursor.parent().hasId());
- TNode parent = cursor.parent();
-
- // we have to take different actions based on if the cursor has a preceding
- // element or not
- TNode prev = cursor.prev();
- if (prev)
- {
- // the cursor has a preceding element, so we try to remove it
- if (special) return drop_prev(special);
- else
- {
- std::string str = drop_prev(special);
-
- // We control if the group has to be removed, because the cursor
- // might be the only element of the group.
- // But we have to be careful, because drop_prev could change the TML tree
- // more than we think...parent could no longer exist!
- parent = cursor.parent();
- if ((parent.first() == cursor) && parent.isG() && parent.hasId())
- rgreplace_father();
-
- return str;
- }
- }
- else
- {
- // the cursor has no preceding elements, so we have to remove the
- // group.
- if (special)
- {
- parent.replace(cursor);
- return "";
- }
- else
- {
- rgreplace_father();
- // we have to re-start the process, because it' a graphical deletion
- return do_drop(special);
- }
- }
-
-} // end of method do_drop_groupId()
-
-std::string
-TPushParser::do_drop_phantom_group(bool special)
-{
- // if we are here, the cursor MUST be a child of a
- // phantom group.
- assert(cursor.parent() && cursor.parent().isG() && !cursor.parent().hasId());
-
- TNode parent = cursor.parent();
-
- // now we have to control if the cursor has a preceding element or not
- TNode prev = cursor.prev();
- if (prev)
- {
- if (parent.parent() && parent.parent().isC())
- {
- // there is a frame in the stack
- assert(!frames.empty());
- if (frames.top().entry.pattern.size())
- {
- Frame& frame = frames.top();
- if (special)
- {
- // we are in a delimited argument. If the user has inserted a proper subset of the
- // delimiters'sequence, we start to remove the previous delimiter. Start to remove
- // a delimiter means that that delimiter must be removed from the count of inserted delimiters.
- // It means that we have to decrement the member pos.
- if (frame.entry.pattern[frame.pos].category != TToken::PARAMETER)
- {
- std::string del = frame.entry.pattern[frame.pos].value;
- frame.pos--;
- return "\\" + del;
- }
- }
- else
- {
- // 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
- std::string str = drop_prev(special);
-
- if (special) return str;
- else
- {
- // now we have to control the parent, to handle the case of primes. But we have returned from a drop_prev(), which
- // could change the TML tree. So not asssuming that cursor's parent is unchanged is convenient.
- parent = cursor.parent();
- if (parent.isG() && !parent.hasId() && (parent.size() == 1) && parent.parent().isSp())
- {
- // in this case the drop_prev has removed the only element preceding the cursor.
- // Since the phantom group is an sp's child, the user has removed all \' in the
- // phantom group.
- // Now we have some possibilities:
- // - we can replace the phantom group with the cursor, giving the user the chance to insert a new
- // exponent
- // - we can remove the phantom group and the sp element, recreating the state before the user inserted the first
- // prime.
- // At the moment we implement the second one.
- assert(parent.parent().size() == 2);
- TNode gparent = parent.parent();
- TNode base = gparent.first();
- cursor.remove();
- parent.remove();
- gparent.replace(base);
- // now base's parent is no more gparent
- base.parent().append(cursor);
-
- return str;
- }
- else if (parent.isG() && !parent.hasId() && parent.parent().isSp())
- {
- // in this case we have to place the cursor after the sp element
- cursor.remove();
- assert(parent.parent().parent());
- parent.parent().parent().append(cursor);
- return str;
- }
- else return str;
- }
- }
- else
- {
- // in this case the cursor is the only element of the phantom group,
- // so we have to remove it. But, a phantom group has a special role,
- // so we have to control the grand father of the cursor.
- TNode gfather = parent.parent();
- if (!gfather)
- {
- // If here, the TML tree is in an inconsistent state
- logger.error("TML tree in a inconsistent state");
- return "";
- }
- else if (gfather.isC())
- {
- // in this case the phantom group is child of a MACRO.
- // We have to control the nature of this MACRO.
- assert(!frames.empty());
- Frame& frame = frames.top();
-
- // this variable is useful in a special deletion
- std::string macro_name = gfather.nameC();
-
- if (frame.entry.leftOpen && frame.entry.rightOpen)
- {
- // in this case, the cursor'parent is in the second and last child
- // of the MACRO. We can assert that the grand father has two
- // children, which are both phantom groups
- assert(gfather.size() == 2);
- assert((gfather.last() == parent) && (gfather.first().isG() && !gfather.first().hasId()));
- assert(frame.pos == 0);
-
- TNode ggfather = gfather.parent();
- assert(ggfather);
- cursor.remove();
- parent.remove();
- // we have to replace the gfather with the elements of its first child, but this group may have no
- // children.
- if (gfather.first().size())
- {
- gfather.replace(gfather.first().first(), TNode());
- ggfather.append(cursor);
- }
- else
- {
- // in this case, the MACRO has to be replaced with the cursor
- gfather.first().remove();
- gfather.replace(cursor);
- }
- // now we have the situation preceding the insertion of the leftOpen and rightOpen MACRO.
- // this MACRO no longer exists.
- frames.pop();
-
- if (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)
- {
- // the user has inserted a rightOpen MACRO, and now, this MACRO has no children (excluding the
- // phantom group), so we remove the MACRO.
- // We can assert that cursor's parent is the only child of the MACRO
- assert(gfather.size() == 1);
- assert(frame.pos == 0);
- cursor.remove();
- parent.remove();
- gfather.replace(cursor);
-
- // now we have the situation preceding the rightOpen MACRO, so we have to pop the frame
- frames.pop();
-
- if (special) return "\\" + macro_name;
- else
- {
- // to give a standard behavior to the graphical deletion, we call the do_drop.
- return do_drop(special);
- }
-
- }
- else 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.
- // We have to control if the cursor's parent has a preceding element,
- // or not.
- TNode uncle = parent.prev();
- if (!uncle)
- {
- // the parent is the only element of the MACRO.
- // we can assert that frame.pos == 0.
- // In this case we can replace the MACRO with the cursor
- assert(frame.pos == 0);
- cursor.remove();
- parent.remove();
- gfather.replace(cursor);
- frames.pop();
-
- if (special) return "\\" + macro_name;
- else
- {
- // once we have replaced the empty macro with the cursor, we can remove
- // something else
- return do_drop(special);
- }
- }
- else
- {
- // the parent has a preceding element. Now we have
- // to control if the uncle is a delimited argument or not.
- if (uncle.isG() && !uncle.hasId())
- {
- // cursor's uncle is a delimited argument
- cursor.remove();
- parent.remove();
- uncle.append(cursor);
- if (special)
- {
- // we have to start removing the last delimiter of the delimited
- // argument.
- std::string last_del = frame.entry.pattern[frame.pos - 1].value;
- frame.pos = frame.pos - 2;
- return "\\" + last_del;
- }
- else
- {
- // the uncle is a delimited argument. So we have to ideally
- // remove the sequence of delimiters.
- assert(frame.pos > 1);
- unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
- assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
- assert(sequence_length);
- // sequence_length is the length of the delimiters sequence which separates
- // the current parameter and the previous parameter
- frame.pos = frame.pos - sequence_length - 1;
-
- // once removed the sequnce of delimiters, we can start to remove the actual
- // parameter. We can call the do_drop_phantom_group() because a delimited argument
- // is always a phantom group's child
- return do_drop_phantom_group(special);
- }
- }
- else
- {
- // the uncle is a not delimited argument, so we try to remove it.
- cursor.remove();
- parent.replace(cursor);
- parent = cursor.parent(); // we update the parent (it should be the MACRO)
- assert(parent.isC());
-
- // now we try to remove the uncle (now it' the preceding element)
- return drop_prev(special);
- }
- } // this is the else's end, that handles the case in which an uncle exists
- } // end of if (!frame.entry.pattern.empty())
- else
- {
- // the entry has no arguments, is not rightOpen and is not leftOpen.
- logger.error("TML tree in a strange state");
- return "";
- }
- } // 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 == 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.
- TNode prev_cell = gfather.prev();
- cursor.remove();
- parent.remove();
- gfather.remove();
- // now the cell no longer exists
-
- if (!prev_cell)
- {
- // in this case, the cell was the only cell in the row.
- // So, we assume that the user wants to delete the entire row.
- TNode table = row.parent();
- TNode prev_row = row.prev();
- row.remove();
-
- if (!prev_row)
- {
- if (special)
- {
- // Since there was a cell (and a row), the user has typed a "{" to
- // We ideally remove this character.
- table.append(cursor);
- return "";
- }
- else
- {
- // the row was the only child of the table.
- // so we have to delete the entire table
- assert(table.parent());
- TNode parent_table = table.parent();
- table.remove();
- frames.pop();
- parent_table.append(cursor);
- return "";
- }
- }
- else
- {
- // there are other rows (one or more)
- assert(prev_row.is("row"));
- assert(prev_row.last());
- TNode last_cell = prev_row.last();
- assert(last_cell.is("cell"));
- assert(last_cell.size() == 1);
- assert(last_cell.first().isG() && !last_cell.first().hasId());
- last_cell.first().append(cursor);
- // Since cells and rows are separated by spaces and CRs
- // (and the user can see this spaces and CRs), a special deletion
- // is equivalent to a normal deletion
- return "";
- }
- } // end of if (!prev_cell)
- else
- {
- // being here means that there is a previous cell,
- // so we append the cursor to group.
- assert(prev_cell.size() == 1);
- assert(prev_cell.first().isG() && !prev_cell.first().hasId());
- prev_cell.first().append(cursor);
- return "";
- }
- } // end of if (gfather.is("cell"))
- else if (gfather.isSp())
- {
- // we cannot be here because a phantom group can be a Sp child only
- // in two cases. If the user has typed somethong like:
- // $^
- // the cursor is not phantom group's child.
- // If the user has typed somethong like
- // ..''
- // In this case the sequence of ' is placed in a phantom group,
- // which becomes the exponent of the script. But, the cursor is
- // always outside the phantom group
- logger.error("TML tree in a strange state");
- return "";
- }
- else if (gfather.is("math"))
- {
- // in this case we ignore the user's will of deleting
- // but we could also decide to remove the math mode.
- logger.warning("nothing to delete");
- return "";
- }
- else
- {
- // cursor's grand father is undefined
- logger.error("TML tree is in an unknown state");
- return "";
- }
- } // end of the else of the if (prev)
-
-}
-
-
-std::string
-TPushParser::do_drop(bool special)
-{
- // we have to handle the case in wich the cursor has a parent or not
- if (!cursor.parent())
- {
- // it's not a good situation...at the moment we do not take actions
- logger.error("TML tree not well structured");
- return "";
- }
- else
- {
- // a parent exists. We have to take differnt actions, based on the nature of
- // the parent
- TNode parent = cursor.parent();
- if (parent.is("math"))
- {
- // we ca do two thing...we can remove the math mode (it implies controlling the display attribute), we can do nothing
- // At the moment, the user's will of deleting is simply ignored
- logger.warning("nothing to delete");
- return "";
- }
- else if (parent.isG())
- {
- // the cursor's parent is a group. We have to control if it's a phantom group or not
- if (parent.hasId())
- {
- return do_drop_groupId(special);
- }
- else
- {
- return do_drop_phantom_group(special);
- }
- } // end of parent is group
- else if (parent.isC())
- {
- return do_drop_macro(special);
- } // end of parent is a MACRO
- else if (parent.isSp() || parent.isSb())
- {
- return do_drop_script(special);
- } // end of parent is sp or sb
- } // end of the else which consider the case in which parent exists
-
-} // end of method do_drop
-
-bool
-TPushParser::process(const TToken& token)
-{
- switch (token.category)
- {
- case TToken::BEGIN: return do_begin();
- case TToken::END: return do_end();
- case TToken::SHIFT: return do_shift();
- case TToken::ALIGN: return do_align();
- case TToken::EOL: return do_eol();
- case TToken::PARAMETER: return do_parameter(token.value);
- case TToken::SUPERSCRIPT: return do_superscript();
- case TToken::SUBSCRIPT: return do_subscript();
- case TToken::IGNORABLE_SPACE: return do_ignorablespace(token.value);
- case TToken::SPACE: return do_space(token.value);
- case TToken::LETTER: return do_letter(token.value);
- case TToken::DIGIT: return do_digit(token.value);
- case TToken::OTHER: return do_other(token.value);
- case TToken::ACTIVE: return do_active(token.value);
- case TToken::COMMENT: return do_comment();
- case TToken::CONTROL: return do_control(token.value);
- }
-}
-
-void
-TPushParser::push(const TToken& token)
-{
- TNode parent = cursor.parent();
- // If the cursor has no parent then it is detached from the editing
- // tree, which means this token will be ignored
-
- if (parent)
- // If the parent is a phantom group and the grand-parent is a
- // control sequence, there are two cases:
- // a. we are parsing a delimited argument of a entry
- // b. we are parsing a side of a right- or left-open entry
- if (parent.isG() && !parent.hasId() && parent.parent().isC())
- {
- // There must be an open frame, for the grand-parent is a control sequence
- assert(!frames.empty());
- Frame& frame = frames.top();
- if (!frame.entry.pattern.empty())
- {
- // The entry pattern is not empty. By our conventions this means
- // the entry cannot be open at either end, hence we are parsing
- // a delimited argument
- assert(frame.pos + 1 < frame.entry.pattern.size());
- assert(frame.entry.pattern[frame.pos + 1].category != TToken::PARAMETER);
- if (frame.entry.pattern[frame.pos + 1] == token)
- {
- // The token matches with a delimiter of the argument,
- // hence we increment the frame.pos
- frame.pos++;
-
- if (frame.entry.lastDelimiter(frame.pos))
- {
- // this delimiter is the last one for the argumet,
- // so the argument is completed
- cursor.remove();
- advance(parent);
- }
- }
- else
- {
- // Delimiter mismatch.
- if (frame.entry.pattern[frame.pos].category != TToken::PARAMETER)
- {
- // in this case, there is a sequence of delimiters that delimitates
- // the argument, and the user has correctly inserted a portion of this
- // sequence, but now has inserted a wrong delimiter.
- // Here, there are some possibilities:
- // - ignore the token, and wait for the correct delimiter
- // - ignore the token, wait for the correct delimiter and emit an error
- // At the moment, we implement the second possibily
- logger.warning("it's not the correct delimiter...you have to type '" + frame.entry.pattern[frame.pos + 1].value + "'");
- }
- else
- {
- // in this case, the sequence of delimiters is composed of one
- // delimiter. It means that we have to process the token
- process(token);
- }
- }
- }
- else
- {
- // The entry pattern is empty, hence we are parsing a right-open
- // entry. What happens if we actually are in the left side?
- // This could happen only when re-editing an entered expression
- // We'll see...
- assert(frame.entry.rightOpen);
- process(token);
- }
- }
- else if (parent.isC())
- {
- // We are parsing a non-delimited argument entry
- // or a fixed token
- Frame& frame = frames.top();
- assert(frame.pos < frame.entry.pattern.size());
-
- if (frame.entry.pattern[frame.pos].category == TToken::PARAMETER)
- {
- // As by the TeX parsing rules of undelimited parameters,
- // empty spaces are ignored
- if ((token.category != TToken::SPACE) && (token.category != TToken::IGNORABLE_SPACE)) process(token);
- }
- else if (frame.entry.pattern[frame.pos] == token)
- {
- // The token has been accepted
- frame.pos++;
- if (frame.pos < frame.entry.pattern.size() &&
- frame.entry.paramDelimited(frame.pos))
- {
- // If the next is a delimited argument we have to place
- // the phantom group with the cursor inside
- TNode g = doc.createG();
- cursor.replace(g);
- g.append(cursor);
- }
- else
- {
- cursor.remove();
- advance(parent);
- }
- }
- else
- {
- // There is a mismatch. Emit an error and ignore the token?
- logger.warning("ignored token: " + token.value);
- }
- }
- else
- process(token);
- else
- {
- logger.warning("ignored token");
- }
-
- if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
-}
-
-std::string
-TPushParser::drop(bool special)
-{
- std::string str = do_drop(special);
- if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
- return str;
-}
-
-void
-TPushParser::advance(const TNode& node)
-{
- assert(node);
-
- if (!node.parent())
- {
- // this is an error
- logger.error("wrong TML tree");
- }
- else if (node.parent().isG())
- {
- TNode next = node.next();
- if (next) next.insert(cursor);
- else node.parent().append(cursor);
- }
- else if (node.parent().isC())
- {
- assert(!frames.empty());
- if ((frames.top().pos + 1 == frames.top().entry.pattern.size()) || (frames.top().entry.pattern.empty()))
- {
- // we are here when we have a right open macro, or the inserted element is the last one
- if (frames.top().entry.rightOpen)
- {
- // we have to remove the frame from the stack
- frames.pop();
- advance(node.parent().parent());
- }
- else
- {
- frames.pop();
- advance(node.parent());
- }
- }
- else if (frames.top().entry.paramDelimited(frames.top().pos + 1))
- {
- // the next argument is delimited, so we have to create a phantom group
- TNode g = doc.createG();
- g.append(cursor);
- node.parent().append(g);
- frames.top().pos++;
- }
- else
- {
- // the next argumet is not delimited, so we have to append the cursor
- // to the MACRO
- node.parent().append(cursor);
- frames.top().pos++;
- }
- }
- else advance(node.parent());
-}
-
-void
-TPushParser::setCursorHint(const std::string& c)
-{
- if (cursor["val"] != c)
- {
- cursor["val"] = c;
- if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
- }
-}
-
-bool
-TPushParser::hideCursor()
-{
- if (hiddenCursor++ == 0)
- {
- cursor["visible"] = "0";
- if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
- return true;
- }
- else
- return false;
-}
-
-bool
-TPushParser::showCursor()
-{
- if (hiddenCursor > 0 && --hiddenCursor == 0)
- {
- cursor["visible"] = "1";
- if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
- return true;
- }
- else
- return false;
-}
-
-bool
-TPushParser::thaw()
-{
- if (APushParser::thaw() && factory && doc.dirtyNode())
- {
- factory->documentModified(doc);
- return true;
- }
- else
- return false;
-}