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());
93 TPushParser::do_shift()
95 TNode parent = cursor.parent();
99 TNode math = doc.create("math", nextId++);
100 TNode g = doc.createG();
101 cursor.replace(math);
105 else if (parent.isG() && !parent.hasId() && parent.parent() && parent.parent().is("math"))
109 // there is something before the cursor, hence this is the
110 // closing math shift
111 if (parent.parent()["display"] != "1")
113 // one math shift is enough to close it
118 // we need two closing math shifts
119 parent.parent().append(cursor);
122 else if (parent.parent()["display"] != "1")
124 // there is nothing before the cursor, and the math is not
125 // in display mode, so this must be a double math shift
126 parent.parent()["display"] = "1";
130 parent.parent().append(cursor);
133 else if (parent.is("math"))
139 cerr << "ERROR: math shift" << endl;
144 TPushParser::do_align()
146 TNode parent = cursor.parent();
147 if (parent && parent.isG() && parent.hasId())
149 // alignment tab used for the first time inside a group
150 TNode row = doc.create("row");
151 TNode cell = doc.create("cell");
152 TNode g = doc.createG();
155 g.append(parent.first(), cursor);
157 else if (parent && parent.isG() && parent.parent().is("cell"))
159 // alignment tab used within a cell
160 TNode oldCell = parent.parent();
161 assert(oldCell && oldCell.is("cell"));
162 TNode row = oldCell.parent();
163 assert(row && row.is("row"));
164 TNode cell = doc.create("cell");
165 if (oldCell.next()) oldCell.next().insert(cell);
166 else row.append(cell);
167 TNode g = doc.createG();
173 cerr << "alignment tab used outside matrix" << endl;
178 TPushParser::do_eol()
180 //if (cursor.parent()) cursor.remove();
184 TPushParser::do_parameter(const std::string& p)
190 TPushParser::do_subscript()
192 TNode parent = cursor.parent();
195 TNode prev = cursor.prev();
198 TNode elem = doc.create("sb", nextId++);
199 TNode g = doc.createG();
200 cursor.replace(elem);
206 TNode elem = doc.create("sb", nextId++);
212 else if (parent.isSb() && cursor == parent[1])
214 if (parent["under"] == "1") cerr << "already under" << endl;
215 else parent["under"] = "1";
220 TPushParser::do_superscript()
222 TNode parent = cursor.parent();
225 TNode prev = cursor.prev();
228 TNode elem = doc.create("sp", nextId++);
229 TNode g = doc.createG();
230 cursor.replace(elem);
236 TNode elem = doc.create("sp", nextId++);
242 else if (parent.isSp() && cursor == parent[1])
244 if (parent["over"] == "1") cerr << "already over" << endl;
245 else parent["over"] = "1";
250 TPushParser::do_space(const std::string&)
252 // ? may be used to distinguish tokens in some mode?
256 TPushParser::do_letter(const std::string& s)
258 //TNode parent = cursor.parent();
259 TNode elem = doc.createI(s, nextId++);
260 cursor.replace(elem);
265 TPushParser::do_digit(const std::string& s)
267 TNode parent = cursor.parent();
268 TNode prev = cursor.prev();
269 if (prev && parent.isG() && prev.is("n"))
271 TNode elem = doc.createN(prev.value() + s, nextId++);
276 TNode elem = doc.createN(s, nextId++);
277 cursor.replace(elem);
283 TPushParser::isPrimes(const TNode& node) const
286 return node.isG() && node.last() && node.last().is("o") && node.last()["val"] == PRIME();
290 TPushParser::do_apostrophe()
292 if (cursor.parent() && cursor.parent().isG())
294 if (TNode prev = cursor.prev())
296 if (prev.isSp() && prev[1] && isPrimes(prev[1]))
297 prev[1].append(doc.createO(PRIME(), nextId++));
298 else if (prev.isSb() && prev[0] &&
299 prev[0].isSp() && prev[0][1] &&
300 isPrimes(prev[0][1]))
301 prev[0][1].append(doc.createO(PRIME(), nextId++));
304 TNode elem = doc.create("sp");
305 TNode g = doc.createG();
309 g.append(doc.createO(PRIME(), nextId++));
324 TPushParser::do_other(const std::string& s)
332 cout << "TPushParser::do_other " << s << endl;
333 cout << "DOCUMENT: " << static_cast<GdomeNode*>(cursor.element().get_ownerDocument()) << endl;
334 TNode elem = doc.createT("o", s, nextId++);
335 cursor.replace(elem);
342 TPushParser::do_active(const std::string&)
348 TPushParser::do_comment()
356 TNode parent = cursor.parent();
357 if (parent && parent.isG() &&
358 parent.parent() && parent.parent().is("cell") &&
359 parent.parent().parent() && parent.parent().parent().is("row"))
361 TNode oldRow = parent.parent().parent();
363 TNode table = oldRow.parent();
365 TNode row = doc.create("row");
366 TNode cell = doc.create("cell");
367 TNode g = doc.createG();
368 if (oldRow.next()) oldRow.next().insert(row);
369 else table.append(row);
377 TPushParser::do_control(const std::string& name)
379 if (name == "cr") do_cr();
382 TNode parent = cursor.parent();
383 const TDictionary::Entry& entry = dictionary.find(name);
386 case TDictionary::IDENTIFIER:
388 TNode t = doc.createI(entry.value, nextId++);
394 case TDictionary::OPERATOR:
396 TNode t = doc.createO(entry.value, nextId++);
402 case TDictionary::NUMBER:
404 TNode t = doc.createN(entry.value, nextId++);
410 case TDictionary::MACRO:
412 TNode m = doc.createC(name, nextId++);
415 cout << "ecco tutti i token del pattern della entry inserita" << endl;
416 for (unsigned i = 0; i < entry.pattern.size(); i++)
417 cout << entry.pattern[i].value << endl;
419 if (entry.leftOpen && entry.rightOpen)
421 assert(entry.pattern.empty());
422 assert(parent.isG());
423 TNode g1 = doc.createG();
424 g1["left-open"] = "1";
425 g1.append(parent.first(), m);
427 TNode g2 = doc.createG();
430 frames.push(Frame(entry));
432 else if (entry.leftOpen)
434 assert(parent.isG());
435 TNode g = doc.createG();
436 g["left-open"] = "1";
437 g.append(parent.first(), m);
441 else if (entry.rightOpen)
443 assert(entry.pattern.empty());
444 assert(parent.isG());
445 TNode g = doc.createG();
448 frames.push(Frame(entry));
450 else if (!entry.pattern.empty())
454 frames.push(Frame(entry));
455 cout << "do_control: valore di pos del frame inserito " << frames.top().pos << endl;
456 if (entry.paramDelimited(0))
458 TNode g = doc.createG();
467 // error, but we could handle this very easily
468 cerr << "error, but we could handle this easily" << endl;
474 case TDictionary::UNDEFINED:
476 cerr << "ERROR: using undefined macro `" << name << "'" << endl;
477 TNode m = doc.createC(name, nextId++);
489 TPushParser::gdelete_prev()
491 // if in this function, the prev of cursor does exist, also the parent and we want a graphical deleting.
493 assert(cursor.prev());
494 assert(cursor.parent());
496 TNode prev = cursor.prev();
497 if (prev.is("i") || prev.is("o") || prev.is("n"))
499 // the control below is designed to handle the case in which val have more than one unicode character
500 DOM::UCS4String ucs4val(prev.element().getAttribute("val"));
501 if ((ucs4val.length() <= 1) || prev.element().hasAttribute("name"))
504 prev.replace(cursor);
506 if (cursor.parent().isC())
508 // in this case we have removed an element of a MACRO.
509 // we can assert that this element was a non delimited argument
510 assert(!frames.empty());
511 Frame& frame = frames.top();
512 assert(frame.pos > 0);
519 ucs4val.erase(ucs4val.length() - 1, 1);
520 prev.element().setAttribute(DOM::GdomeString("val"), DOM::GdomeString(ucs4val));
522 } // end of if (prev.is("i") || ...)
523 else if (prev.is("sp") || prev.is("sb"))
528 } // end of if (prev.is("sp") || prev.is("sb"))
537 const TDictionary::Entry& entry = dictionary.find(prev["name"]);
539 // i start to remove a MACRO. The frame associated to this MACRO was
540 // popped from the stack (i think). So, i re-push the frame in the stack,
541 // but the pos member should be set correctly
542 cout << "gdelete_prev: i have to start to delete a MACRO" << endl;
544 // if the control element is leftOpen and rightOpen, the cursor should be placed after
545 // the last child of the control element's last child
548 cout << "gdelte_prev(): i have to delete a control rightOpen, so i push an element in the stack" << endl;
553 prev.last().append(cursor);
556 else if (entry.leftOpen)
558 cout << "gdelete_prev: i have to delete a control element with leftOpen" << endl;
559 assert(prev.first());
560 assert(prev.first().isG());
561 assert(prev.first() == prev.last());
562 TNode g = prev.first();
564 prev.replace(g.first(), TNode());
566 else if (!entry.pattern.empty())
568 // we have to start removing a MACRO which accepts arguments.
569 // a MACRO without child does not exist
571 cout << "gdelete_prev: i have to remove a MACRO with argument" << endl;
573 assert(prev.size() >= 1);
575 if (prev.last().isG() && !prev.last().hasId())
577 // this means that the last child of the MACRO is a phantom group,
578 // which in turn means that it is a delimited argument
579 // so we have to ideally remove this delimiter
581 prev.last().append(cursor);
582 // i have to push a frame with a correct value of pos
584 frame.pos = entry.pattern.size() - 2;
589 // in this case, the last child of the MACRO is
590 // an argument which is NOT delimited, so i try to
592 cout << "gdelete_prev: i try to remove the last argumet of the MACRO" << endl;
594 prev.append(cursor); // now prev is the cursor's parent
597 frame.pos = entry.pattern.size();
613 TPushParser::rgreplace_futher(void)
615 // this function MUST only be invoked, when the cursor
616 // is the only child of a group with id. This function
617 // replace the group with the cursor. But if the new parent
618 // is a group with id and the cursor is the only child of the
619 // group, the new parent is replaced...and so on.
620 // r stands for recursive, g stands for graphical
621 assert(cursor.parent());
622 assert(cursor.parent().isG() && cursor.parent().hasId());
624 TNode parent = cursor.parent();
626 while (parent.isG() && parent.hasId() && (parent.first() == cursor))
628 parent.replace(cursor);
629 parent = cursor.parent();
634 // in this case we have removed a MACRO's child.
635 // I can assert that this MACRO accepts arguments.
636 assert(!frames.empty());
637 Frame& frame = frames.top();
638 assert(frame.pos > 0);
644 TPushParser::do_gdelete()
646 // this function operates a graphical deleting
648 //if (!frames.empty())
649 // cout << "do_gdelete: c'e' un frame aperto e il suo pos vale: " << frames.top().pos << endl;
651 TNode parent = cursor.parent();
653 // if no parent, do nothing
659 TNode prev = cursor.prev();
662 // i try to delete the preceding element
665 if ((parent.first() == cursor) && parent.isG() && parent.hasId())
669 else // no previous node is present
671 // if here, we are in a gruop whose only child is the cursor.
675 // the parent is a phantom group
676 assert(parent.parent());
677 if (!parent.parent().is("math"))
679 TNode gfuther = parent.parent();
681 // if the grand futher is a group with Id, it should be removed,
682 // but i don't know if it will never occur...
683 if (gfuther.isG() && gfuther.hasId())
686 parent.replace(cursor);
688 // re-start the process
691 else if (gfuther.isC())
693 // the grand futher is a control element: since the parent is a phantom group,
694 // the TML tree should be in a inconsistent state (once removed the parent).
696 // being here means that there is a frame in the stack
697 assert(!frames.empty());
698 cout << "do_gdelete: i have to remove a phantom group whuch is a child of a MACRO" << endl;
699 Frame& frame = frames.top();
700 if (frame.entry.leftOpen && frame.entry.rightOpen)
702 // in this case, the cursor is in the second and last child
703 // of the MACRO. We can assert that the grand futher has two
704 // children. which are both phantom group
705 cout << "do_gdelete: the MACRO is leftOpen and rigthOpen" << endl;
706 assert(gfuther.size() == 2);
707 assert((gfuther.last() == parent) && (gfuther.first().isG() && !gfuther.first().hasId()));
708 assert(frame.pos == 0);
710 TNode ggfuther = gfuther.parent();
714 // i have to replace the gfuther with the elements of its first child
715 gfuther.replace(gfuther.first().first(), TNode());
716 cout << "do_gdelete: i have removed the control element, and replaced it with its first child" << endl;
717 ggfuther.append(cursor);
718 cout << "do_gdelete: cursor appended to the grand grand futher" << endl;
720 // now we have the situation preceding the insertion of the MACRO leftOpen and rightOpen
721 // this MACRO no longer exists.
724 else if (frame.entry.rightOpen)
726 // the user has inserted a MACRO rightOpen. Since the cursor is the
727 // only child of the MACRO, the user want to remove it.
728 // We can assert that cursor's parent is the only child of the MACRO
729 cout << "do_gdelete: the MACRO is rightOpen only" << endl;
730 assert(gfuther.size() == 1);
731 assert(frame.pos == 0); // i think this assert has no sense
735 gfuther.replace(cursor);
737 // now we have the situation preceding the MACRO rightOpen, so i have to pop the frame
740 else if (frame.entry.leftOpen)
742 // it' s an unpredicted situation
743 cout << "it's a bad situation, maybe handlable, but unpredicted" << endl;
745 else if (!frame.entry.pattern.empty())
747 // the MACRO (the cursor's grand futher) accepts arguments.
748 // we have to control if the cursor's uncle does exist.
752 // in this case, we can assert that frame in the stack has
753 // pos greater than 0
754 assert(frame.pos > 0);
756 // cursor's uncle does exist. we have to control
757 // its nature (is it a phantom group?)
758 TNode uncle = parent.prev();
759 if (uncle.isG() && !uncle.hasId())
761 // the cursor's parent is a phantom group, so it was a
762 // delimited argument of the MACRO and the corrisponding
763 // delimeter is inserted. So, the action of deleting means
764 // removing this delimeter
767 uncle.append(cursor);
772 // the uncle was a NOT delimited argument. So i try to
775 parent.replace(cursor);
777 parent = cursor.parent(); // i update the parent (it should be the MACRO)
778 assert(parent.isC());
786 // cursor's parent is the only child of the MACRO, which accepts arguments
787 // i can assert that frame.pos == 0.
788 // In this case i can replace the MACRO with the cursor
789 assert(frame.pos == 0);
793 gfuther.replace(cursor);
801 else // the grand futher is math
803 // nothing to do...i think
804 assert(frames.empty());
809 // the parent is a group with id and has no elements other than cursor
810 // so we replace it with the cursor.
813 // i have to re-start the process, because it' a graphical delete
818 else if (parent.isC())
820 // being here means that there is an open frame for the control element
821 // and this element is closed at either side (no leftOpen no rightOpen)
822 // and the MACRO was waiting for a non delimited argument, so
823 // we can assert that frame.entry.pattern.size() >= 1
824 assert(!frames.empty());
825 Frame& frame = frames.top();
827 assert(frame.entry.pattern.size() >= 1);
829 cout << "do_gdelete: frames.top().pos = " << frames.top().pos << endl;
831 TNode prev = cursor.prev();
835 // in this case we can replace the MACRO with the cursor
836 // and pop the stack and we can assert that frame.pos == 0
837 assert(frame.pos == 0);
838 cursor.remove(); // it should not be necessary, but i'm not shure
839 parent.replace(cursor);
844 // in this case the cursor has a preceding element
845 // and there are differnt things based on the nature
846 // of the prev: if it's a phantom group do something,
847 // else do something else
848 if (prev.isG() && !prev.hasId())
850 // in this case we have to append the cursor
851 // to the prev and decrement frame.pos of two units
852 // because the prev is a delimited argument and the
853 // delimiter is inserted. So we ideally remove this delimiter
860 // the prev is an non delimited argument, so we try to
866 else if (parent.is("sp") || parent.is("sb"))
868 // being here means that a prev MUST exist
869 // and that there is only an element preceding the cursor.
870 // A sp's (or sb's) MUST NOT be a MACRO
872 assert(parent.size() == 2);
873 assert(parent.parent() && !parent.parent().isC());
875 TNode prev = cursor.prev();
877 if (prev.isG() && !prev.hasId() && (prev.size() == 0))
880 parent.replace(cursor);
882 // now, cursor should be the only parent's child
883 assert(cursor.parent().size() == 1);
885 if (cursor.parent().isG() && cursor.parent().hasId()) rgreplace_futher();
889 assert(prev.hasId());
890 parent.replace(prev);
891 prev.parent().append(cursor);
892 // now prev should have a preceding element
893 assert(cursor.parent().size() > 1);
898 // not handled: no control for tables, ...
903 // the cursro has no parent!!!
905 // emit an error? and if we want to emit an error, in which way?
911 TPushParser::process(const TToken& token)
913 switch (token.category)
915 case TToken::BEGIN: do_begin(); break;
916 case TToken::END: do_end(); break;
917 case TToken::SHIFT: do_shift(); break;
918 case TToken::ALIGN: do_align(); break;
919 case TToken::EOL: do_eol(); break;
920 case TToken::PARAMETER: do_parameter(token.value); break;
921 case TToken::SUPERSCRIPT: do_superscript(); break;
922 case TToken::SUBSCRIPT: do_subscript(); break;
923 case TToken::SPACE: do_space(token.value); break;
924 case TToken::LETTER: do_letter(token.value); break;
925 case TToken::DIGIT: do_digit(token.value); break;
926 case TToken::OTHER: do_other(token.value); break;
927 case TToken::ACTIVE: do_active(token.value); break;
928 case TToken::COMMENT: do_comment(); break;
929 case TToken::CONTROL: do_control(token.value); break;
930 case TToken::GDELETE: do_gdelete(); break;
935 TPushParser::push(const TToken& token)
937 cerr << "TPushParser::push " << token.value << " (cat: " << token.category << ")" << endl;
939 if (token.category == TToken::GDELETE)
941 cout << "push: i have to process a token with category member = GDELETE" << endl;
947 TNode parent = cursor.parent();
948 // If the cursor has no parent then it is detached from the editing
949 // tree, which means this token will be ignored
952 // If the parent is a phantom group and the grand-parent is a
953 // control sequence, there are two cases:
954 // a. we are parsing a delimited argument of a entry
955 // b. we are parsing a side of a right- or left-open entry
956 if (parent.isG() && !parent.hasId() && parent.parent().isC())
958 // There must be an open frame, for the grand-parent is a control sequence
959 assert(!frames.empty());
960 Frame& frame = frames.top();
961 if (!frame.entry.pattern.empty())
963 // The entry pattern is not empty. By our conventions this means
964 // the entry cannot be open at either end, hence we are parsing
965 // a delimited argument
966 assert(frame.pos + 1 < frame.entry.pattern.size());
967 assert(frame.entry.pattern[frame.pos + 1].category != TToken::PARAMETER);
968 if (frame.entry.pattern[frame.pos + 1] == token)
970 // The token matches with the delimiter of the argument.
973 // If the phantom group has just one child, it is not necessary
974 // and can be removed. However, this would complicate
975 // all the code that follows, so given it is a rare case we
977 // if (parent.size() == 1) parent.replace(parent[0]);
979 // Eat both the argument and its delimiter
981 if (frame.pos == frame.entry.pattern.size())
983 // This token has completed the entry
984 advance(parent.parent());
986 else if (frame.entry.paramDelimited(frame.pos))
988 // For the next is a delimited argument we have to place
989 // a suitable phantom group with the cursor inside
990 TNode g = doc.createG();
991 parent.parent().append(g);
995 parent.parent().append(cursor);
999 // Delimiter mismatch. Since we are parsing a delimited
1000 // argument we just process the token
1006 // The entry pattern is empty, hence we are parsing a right-open
1007 // entry. What happens if we actually are in the left side?
1008 // This could happen only when re-editing an entered expression
1010 assert(frame.entry.rightOpen);
1014 else if (parent.isC())
1016 // We are parsing a non-delimited argument entry
1018 Frame& frame = frames.top();
1019 assert(frame.pos < frame.entry.pattern.size());
1021 cout << "push: il valore di pos del frame inserito prima e': " << frame.pos << endl;
1022 if (frame.entry.pattern[frame.pos].category == TToken::PARAMETER)
1024 // As by the TeX parsing rules of undelimited parameters,
1025 // empty spaces are ignored
1026 if (token.category != TToken::SPACE)
1028 // We need to increase the frame position here, becase inside
1029 // process the function advance will be called. At that point
1030 // it will be important for the parser to know that the entry
1031 // has been completed in order to place the cursor correctly
1032 // in the next position
1037 else if (frame.entry.pattern[frame.pos] == token)
1039 // The token has been accepted
1041 if (frame.pos < frame.entry.pattern.size() &&
1042 frame.entry.paramDelimited(frame.pos))
1044 // If the next is a delimited argument we have to place
1045 // the phantom group with the cursor inside
1046 TNode g = doc.createG();
1055 // There is a mismatch. Emit an error and ignore the token?
1056 cerr << "ERROR: token ignored: " << token.value << " (cat: " << token.category << ")" << endl;
1064 cout << "ignored token" << endl;
1067 } // this end corresponds to the else of the if (token.category == GDELETE)
1069 if (listener) listener->callback(doc);
1071 if (frames.empty()) cout << "stack vuoto" << endl;
1072 else cout << "stack non vuoto" << endl;
1076 TPushParser::advance(const TNode& node)
1079 TNode parent = node.parent();
1081 ; // nothing to do, the cursor is not in the document any more
1082 else if (parent.isG())
1084 TNode next = node.next();
1085 if (next) next.insert(cursor);
1086 else parent.append(cursor);
1088 else if (parent.isC())
1094 Frame& frame = frames.top();
1095 if (frame.pos == frame.entry.pattern.size())
1101 parent.append(cursor);
1104 else if (parent.is("math"))
1111 TPushParser::setCursor(const std::string& c)
1114 if (listener) listener->callback(doc);