2 #include "TPushParser.hh"
3 #include "TListener.hh"
5 TPushParser::TPushParser(const TDictionary& d) : dictionary(d), listener(0)
10 TPushParser::TPushParser(const TDictionary& d, TListener& l) : dictionary(d), listener(&l)
19 cursor = doc.create("cursor");
22 doc.root().append(cursor);
25 TPushParser::~TPushParser()
30 TPushParser::PRIME() const
32 const TDictionary::Entry entry = dictionary.find("prime");
33 if (entry.cls == TDictionary::OPERATOR) return entry.value;
38 TPushParser::do_begin()
40 TNode parent = cursor.parent();
41 if (parent.isC() && dictionary.find(parent.nameC()).table)
43 TNode row = doc.create("row");
44 TNode cell = doc.create("cell");
45 TNode g = doc.createG();
53 TNode g = doc.createG(nextId++);
62 TNode parent = cursor.parent();
63 if (parent && parent.isG() && parent.hasId())
65 // normal closing brace for an explicitly open group
69 else if (parent && parent.isG() && parent.parent() && parent.parent().is("cell"))
71 // closing brace for a structure in which & or \cr have been used
72 TNode row = parent.parent().parent();
73 assert(row && row.is("row"));
74 TNode table = row.parent();
78 else if (parent && parent.isG() && !parent.hasId() && parent.parent())
80 // closing brace for a right-open macro (like \over)
82 advance(parent.parent());
92 TPushParser::do_shift()
94 TNode parent = cursor.parent();
98 TNode math = doc.create("math", nextId++);
99 TNode g = doc.createG();
100 cursor.replace(math);
104 else if (parent.isG() && !parent.hasId() && parent.parent() && parent.parent().is("math"))
108 // there is something before the cursor, hence this is the
109 // closing math shift
110 if (parent.parent()["display"] != "1")
112 // one math shift is enough to close it
117 // we need two closing math shifts
118 parent.parent().append(cursor);
121 else if (parent.parent()["display"] != "1")
123 // there is nothing before the cursor, and the math is not
124 // in display mode, so this must be a double math shift
125 parent.parent()["display"] = "1";
129 parent.parent().append(cursor);
132 else if (parent.is("math"))
138 cerr << "ERROR: math shift" << endl;
143 TPushParser::do_align()
145 TNode parent = cursor.parent();
146 if (parent && parent.isG() && parent.hasId())
148 // alignment tab used for the first time inside a group
149 TNode row = doc.create("row");
150 TNode cell = doc.create("cell");
151 TNode g = doc.createG();
154 g.append(parent.first(), cursor);
156 else if (parent && parent.isG() && parent.parent().is("cell"))
158 // alignment tab used within a cell
159 TNode oldCell = parent.parent();
160 assert(oldCell && oldCell.is("cell"));
161 TNode row = oldCell.parent();
162 assert(row && row.is("row"));
163 TNode cell = doc.create("cell");
164 if (oldCell.next()) oldCell.next().insert(cell);
165 else row.append(cell);
166 TNode g = doc.createG();
172 cerr << "alignment tab used outside matrix" << endl;
177 TPushParser::do_eol()
179 //if (cursor.parent()) cursor.remove();
183 TPushParser::do_parameter(const std::string& p)
189 TPushParser::do_subscript()
191 TNode parent = cursor.parent();
194 TNode prev = cursor.prev();
197 TNode elem = doc.create("sb", nextId++);
198 TNode g = doc.createG();
199 cursor.replace(elem);
205 TNode elem = doc.create("sb", nextId++);
211 else if (parent.isSb() && cursor == parent[1])
213 if (parent["under"] == "1") cerr << "already under" << endl;
214 else parent["under"] = "1";
219 TPushParser::do_superscript()
221 TNode parent = cursor.parent();
224 TNode prev = cursor.prev();
227 TNode elem = doc.create("sp", nextId++);
228 TNode g = doc.createG();
229 cursor.replace(elem);
235 TNode elem = doc.create("sp", nextId++);
241 else if (parent.isSp() && cursor == parent[1])
243 if (parent["over"] == "1") cerr << "already over" << endl;
244 else parent["over"] = "1";
249 TPushParser::do_space(const std::string&)
251 // ? may be used to distinguish tokens in some mode?
255 TPushParser::do_letter(const std::string& s)
257 //TNode parent = cursor.parent();
258 TNode elem = doc.createI(s, nextId++);
259 cursor.replace(elem);
264 TPushParser::do_digit(const std::string& s)
266 TNode parent = cursor.parent();
267 TNode prev = cursor.prev();
268 if (prev && parent.isG() && prev.is("n"))
270 TNode elem = doc.createN(prev.value() + s, nextId++);
275 TNode elem = doc.createN(s, nextId++);
276 cursor.replace(elem);
282 TPushParser::isPrimes(const TNode& node) const
285 return node.isG() && node.last() && node.last().is("o") && node.last()["val"] == PRIME();
289 TPushParser::do_apostrophe()
291 if (cursor.parent() && cursor.parent().isG())
293 if (TNode prev = cursor.prev())
295 if (prev.isSp() && prev[1] && isPrimes(prev[1]))
296 prev[1].append(doc.createO(PRIME(), nextId++));
297 else if (prev.isSb() && prev[0] &&
298 prev[0].isSp() && prev[0][1] &&
299 isPrimes(prev[0][1]))
300 prev[0][1].append(doc.createO(PRIME(), nextId++));
303 TNode elem = doc.create("sp");
304 TNode g = doc.createG();
308 g.append(doc.createO(PRIME(), nextId++));
323 TPushParser::do_other(const std::string& s)
331 cout << "TPushParser::do_other " << s << endl;
332 cout << "DOCUMENT: " << static_cast<GdomeNode*>(cursor.element().get_ownerDocument()) << endl;
333 TNode elem = doc.createT("o", s, nextId++);
334 cursor.replace(elem);
341 TPushParser::do_active(const std::string&)
347 TPushParser::do_comment()
355 TNode parent = cursor.parent();
356 if (parent && parent.isG() &&
357 parent.parent() && parent.parent().is("cell") &&
358 parent.parent().parent() && parent.parent().parent().is("row"))
360 TNode oldRow = parent.parent().parent();
362 TNode table = oldRow.parent();
364 TNode row = doc.create("row");
365 TNode cell = doc.create("cell");
366 TNode g = doc.createG();
367 if (oldRow.next()) oldRow.next().insert(row);
368 else table.append(row);
376 TPushParser::do_control(const std::string& name)
378 if (name == "cr") do_cr();
381 TNode parent = cursor.parent();
382 const TDictionary::Entry& entry = dictionary.find(name);
385 case TDictionary::IDENTIFIER:
387 TNode t = doc.createI(entry.value, nextId++);
393 case TDictionary::OPERATOR:
395 TNode t = doc.createO(entry.value, nextId++);
401 case TDictionary::NUMBER:
403 TNode t = doc.createN(entry.value, nextId++);
409 case TDictionary::MACRO:
411 TNode m = doc.createC(name, nextId++);
413 if (entry.leftOpen && entry.rightOpen)
415 assert(entry.pattern.empty());
416 assert(parent.isG());
417 TNode g1 = doc.createG();
418 g1["left-open"] = "1";
419 g1.append(parent.first(), m);
421 TNode g2 = doc.createG();
424 frames.push(Frame(entry));
426 else if (entry.leftOpen)
428 assert(parent.isG());
429 TNode g = doc.createG();
430 g["left-open"] = "1";
431 g.append(parent.first(), m);
435 else if (entry.rightOpen)
437 assert(entry.pattern.empty());
438 assert(parent.isG());
439 TNode g = doc.createG();
442 frames.push(Frame(entry));
444 else if (!entry.pattern.empty())
448 frames.push(Frame(entry));
449 if (entry.paramDelimited(0))
451 TNode g = doc.createG();
460 // error, but we could handle this very easily
461 cerr << "error, but we could handle this easily" << endl;
467 case TDictionary::UNDEFINED:
469 cerr << "ERROR: using undefined macro `" << name << "'" << endl;
470 TNode m = doc.createC(name, nextId++);
482 TPushParser::gdelete_prev()
484 // if in this function, the prev of cursor does exist, also the parent and we want a graphical deleting
485 TNode prev = cursor.prev();
486 if (prev.is("i") || prev.is("o") || prev.is("n"))
488 // the control below is designed to handle the case in which val have more than one unicode character
489 DOM::UCS4String ucs4val(prev.element().getAttribute("val"));
490 if (ucs4val.length() <= 1)
493 prev.replace(cursor);
497 ucs4val.erase(ucs4val.length() - 1, 1);
498 prev.element().setAttribute(DOM::GdomeString("val"), DOM::GdomeString(ucs4val));
500 } // end of if (prev.is("i") || ...)
501 else if (prev.is("sp") || prev.is("sb"))
506 } // end of if (prev.is("sp") || prev.is("sb"))
516 // prev.append(cursor);
527 TPushParser::do_gdelete()
529 // this function operates a graphical deleting
531 TNode parent = cursor.parent();
533 // if no parent, do nothing
539 TNode prev = cursor.prev();
542 // i try to delete the preceding element
545 // if gdelete_prev has removed all parent's child, and the group has Id, it should be removed.
546 // this control should be re-done on the new parent and so on.
547 while ((parent.first() == cursor) && parent.isG() && parent.hasId())
550 parent.replace(cursor);
551 parent = cursor.parent();
554 else // no previous node is present
556 if (!parent.parent().is("math"))
559 parent.replace(cursor);
561 // if the new parent is a group with Id, it should be removed
562 if (cursor.parent().isG() && cursor.parent().hasId()) do_gdelete();
566 // nothing to do...i think
570 else if (parent.isC())
572 // i think i have to implement different behaviors based on the attribute name and/or left-open
574 else if (parent.is("sp") || parent.is("sb"))
576 // being here means that a prev MUST exist
578 TNode prev = cursor.prev();
580 if (!prev) cout << "qualcosa non va" << endl;
583 if (parent.first().next() == cursor) // there's only an element preceding the cursor
586 parent.replace(prev);
587 prev.parent().append(cursor);
589 else // there are two elements preceding the cursor
597 // not handled: no control for tables, ...
608 TPushParser::process(const TToken& token)
610 switch (token.category)
612 case TToken::BEGIN: do_begin(); break;
613 case TToken::END: do_end(); break;
614 case TToken::SHIFT: do_shift(); break;
615 case TToken::ALIGN: do_align(); break;
616 case TToken::EOL: do_eol(); break;
617 case TToken::PARAMETER: do_parameter(token.value); break;
618 case TToken::SUPERSCRIPT: do_superscript(); break;
619 case TToken::SUBSCRIPT: do_subscript(); break;
620 case TToken::SPACE: do_space(token.value); break;
621 case TToken::LETTER: do_letter(token.value); break;
622 case TToken::DIGIT: do_digit(token.value); break;
623 case TToken::OTHER: do_other(token.value); break;
624 case TToken::ACTIVE: do_active(token.value); break;
625 case TToken::COMMENT: do_comment(); break;
626 case TToken::CONTROL: do_control(token.value); break;
627 case TToken::GDELETE: do_gdelete(); break;
632 TPushParser::push(const TToken& token)
634 cerr << "TPushParser::push " << token.value << " (cat: " << token.category << ")" << endl;
635 TNode parent = cursor.parent();
636 // If the cursor has no parent then it is detached from the editing
637 // tree, which means this token will be ignored
639 // If the parent is a phantom group and the grand-parent is a
640 // control sequence, there are two cases:
641 // a. we are parsing a delimited argument of a entry
642 // b. we are parsing a side of a right- or left-open entry
643 if (parent.isG() && !parent.hasId() && parent.parent().isC())
645 // There must be an open frame, for the grand-parent is a control sequence
646 assert(!frames.empty());
647 Frame& frame = frames.top();
648 if (!frame.entry.pattern.empty())
650 // The entry pattern is not empty. By our conventions this means
651 // the entry cannot be open at either end, hence we are parsing
652 // a delimited argument
653 assert(frame.pos + 1 < frame.entry.pattern.size());
654 assert(frame.entry.pattern[frame.pos + 1].category != TToken::PARAMETER);
655 if (frame.entry.pattern[frame.pos + 1] == token)
657 // The token matches with the delimiter of the argument.
660 // If the phantom group has just one child, it is not necessary
661 // and can be removed. However, this would complicate
662 // all the code that follows, so given it is a rare case we
664 // if (parent.size() == 1) parent.replace(parent[0]);
666 // Eat both the argument and its delimiter
668 if (frame.pos == frame.entry.pattern.size())
670 // This token has completed the entry
671 advance(parent.parent());
673 else if (frame.entry.paramDelimited(frame.pos))
675 // For the next is a delimited argument we have to place
676 // a suitable phantom group with the cursor inside
677 TNode g = doc.createG();
678 parent.parent().append(g);
682 parent.parent().append(cursor);
686 // Delimiter mismatch. Since we are parsing a delimited
687 // argument we just process the token
693 // The entry pattern is empty, hence we are parsing a right-open
694 // entry. What happens if we actually are in the left side?
695 // This could happen only when re-editing an entered expression
697 assert(frame.entry.rightOpen);
701 else if (parent.isC())
703 // We are parsing a non-delimited argument entry
705 Frame& frame = frames.top();
706 assert(frame.pos < frame.entry.pattern.size());
707 if (frame.entry.pattern[frame.pos].category == TToken::PARAMETER)
709 // As by the TeX parsing rules of undelimited parameters,
710 // empty spaces are ignored
711 if (token.category != TToken::SPACE)
713 // We need to increase the frame position here, becase inside
714 // process the function advance will be called. At that point
715 // it will be important for the parser to know that the entry
716 // has been completed in order to place the cursor correctly
717 // in the next position
722 else if (frame.entry.pattern[frame.pos] == token)
724 // The token has been accepted
726 if (frame.pos < frame.entry.pattern.size() &&
727 frame.entry.paramDelimited(frame.pos))
729 // If the next is a delimited argument we have to place
730 // the phantom group with the cursor inside
731 TNode g = doc.createG();
740 // There is a mismatch. Emit an error and ignore the token?
741 cerr << "ERROR: token ignored: " << token.value << " (cat: " << token.category << ")" << endl;
749 cout << "ignored token" << endl;
752 if (listener) listener->callback(doc);
756 TPushParser::advance(const TNode& node)
759 TNode parent = node.parent();
761 ; // nothing to do, the cursor is not in the document any more
762 else if (parent.isG())
764 TNode next = node.next();
765 if (next) next.insert(cursor);
766 else parent.append(cursor);
768 else if (parent.isC())
774 Frame& frame = frames.top();
775 if (frame.pos == frame.entry.pattern.size())
781 parent.append(cursor);
784 else if (parent.is("math"))
791 TPushParser::setCursor(const std::string& c)
794 if (listener) listener->callback(doc);