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::process(const TToken& token)
484 switch (token.category)
486 case TToken::BEGIN: do_begin(); break;
487 case TToken::END: do_end(); break;
488 case TToken::SHIFT: do_shift(); break;
489 case TToken::ALIGN: do_align(); break;
490 case TToken::EOL: do_eol(); break;
491 case TToken::PARAMETER: do_parameter(token.value); break;
492 case TToken::SUPERSCRIPT: do_superscript(); break;
493 case TToken::SUBSCRIPT: do_subscript(); break;
494 case TToken::SPACE: do_space(token.value); break;
495 case TToken::LETTER: do_letter(token.value); break;
496 case TToken::DIGIT: do_digit(token.value); break;
497 case TToken::OTHER: do_other(token.value); break;
498 case TToken::ACTIVE: do_active(token.value); break;
499 case TToken::COMMENT: do_comment(); break;
500 case TToken::CONTROL: do_control(token.value); break;
505 TPushParser::push(const TToken& token)
507 cerr << "TPushParser::push " << token.value << " (cat: " << token.category << ")" << endl;
508 TNode parent = cursor.parent();
509 // If the cursor has no parent then it is detached from the editing
510 // tree, which means this token will be ignored
512 // If the parent is a phantom group and the grand-parent is a
513 // control sequence, there are two cases:
514 // a. we are parsing a delimited argument of a entry
515 // b. we are parsing a side of a right- or left-open entry
516 if (parent.isG() && !parent.hasId() && parent.parent().isC())
518 // There must be an open frame, for the grand-parent is a control sequence
519 assert(!frames.empty());
520 Frame& frame = frames.top();
521 if (!frame.entry.pattern.empty())
523 // The entry pattern is not empty. By our conventions this means
524 // the entry cannot be open at either end, hence we are parsing
525 // a delimited argument
526 assert(frame.pos + 1 < frame.entry.pattern.size());
527 assert(frame.entry.pattern[frame.pos + 1].category != TToken::PARAMETER);
528 if (frame.entry.pattern[frame.pos + 1] == token)
530 // The token matches with the delimiter of the argument.
533 // If the phantom group has just one child, it is not necessary
534 // and can be removed. However, this would complicate
535 // all the code that follows, so given it is a rare case we
537 // if (parent.size() == 1) parent.replace(parent[0]);
539 // Eat both the argument and its delimiter
541 if (frame.pos == frame.entry.pattern.size())
543 // This token has completed the entry
544 advance(parent.parent());
546 else if (frame.entry.paramDelimited(frame.pos))
548 // For the next is a delimited argument we have to place
549 // a suitable phantom group with the cursor inside
550 TNode g = doc.createG();
551 parent.parent().append(g);
555 parent.parent().append(cursor);
559 // Delimiter mismatch. Since we are parsing a delimited
560 // argument we just process the token
566 // The entry pattern is empty, hence we are parsing a right-open
567 // entry. What happens if we actually are in the left side?
568 // This could happen only when re-editing an entered expression
570 assert(frame.entry.rightOpen);
574 else if (parent.isC())
576 // We are parsing a non-delimited argument entry
578 Frame& frame = frames.top();
579 assert(frame.pos < frame.entry.pattern.size());
580 if (frame.entry.pattern[frame.pos].category == TToken::PARAMETER)
582 // As by the TeX parsing rules of undelimited parameters,
583 // empty spaces are ignored
584 if (token.category != TToken::SPACE)
586 // We need to increase the frame position here, becase inside
587 // process the function advance will be called. At that point
588 // it will be important for the parser to know that the entry
589 // has been completed in order to place the cursor correctly
590 // in the next position
595 else if (frame.entry.pattern[frame.pos] == token)
597 // The token has been accepted
599 if (frame.pos < frame.entry.pattern.size() &&
600 frame.entry.paramDelimited(frame.pos))
602 // If the next is a delimited argument we have to place
603 // the phantom group with the cursor inside
604 TNode g = doc.createG();
613 // There is a mismatch. Emit an error and ignore the token?
614 cerr << "ERROR: token ignored: " << token.value << " (cat: " << token.category << ")" << endl;
622 cout << "ignored token" << endl;
625 if (listener) listener->callback(doc);
629 TPushParser::advance(const TNode& node)
632 TNode parent = node.parent();
634 ; // nothing to do, the cursor is not in the document any more
635 else if (parent.isG())
637 TNode next = node.next();
638 if (next) next.insert(cursor);
639 else parent.append(cursor);
641 else if (parent.isC())
647 Frame& frame = frames.top();
648 if (frame.pos == frame.entry.pattern.size())
654 parent.append(cursor);
657 else if (parent.is("math"))
664 TPushParser::setCursor(const std::string& c)
667 if (listener) listener->callback(doc);