]> matita.cs.unibo.it Git - helm.git/blob - helm/DEVEL/mathml_editor/src/TPushParser.cc
Initial revision
[helm.git] / helm / DEVEL / mathml_editor / src / TPushParser.cc
1
2 #include "TPushParser.hh"
3 #include "TListener.hh"
4
5 TPushParser::TPushParser(const TDictionary& d) : dictionary(d), listener(0)
6 {
7   init();
8 }
9
10 TPushParser::TPushParser(const TDictionary& d, TListener& l) : dictionary(d), listener(&l)
11 {
12   init();
13 }
14
15 void
16 TPushParser::init()
17 {
18   nextId = 1;
19   cursor = doc.create("cursor");
20   cursor["id"] = "I0";
21   doc.clearDirty();
22   doc.root().append(cursor);
23 }
24
25 TPushParser::~TPushParser()
26 {
27 }
28
29 std::string
30 TPushParser::PRIME() const
31 {
32   const TDictionary::Entry entry = dictionary.find("prime");
33   if (entry.cls == TDictionary::OPERATOR) return entry.value;
34   else return "?";
35 }
36
37 void
38 TPushParser::do_begin()
39 {
40   TNode parent = cursor.parent();
41   if (parent.isC() && dictionary.find(parent.nameC()).table)
42     {
43       TNode row = doc.create("row");
44       TNode cell = doc.create("cell");
45       TNode g = doc.createG();
46       row.append(cell);
47       cell.append(g);
48       g.append(cursor);
49       parent.append(row);
50     }
51   else
52     {
53       TNode g = doc.createG(nextId++);
54       cursor.replace(g);
55       g.append(cursor);
56     }
57 }
58
59 void
60 TPushParser::do_end()
61 {
62   TNode parent = cursor.parent();
63   if (parent && parent.isG() && parent.hasId())
64     {
65       // normal closing brace for an explicitly open group
66       cursor.remove();
67       advance(parent);
68     }
69   else if (parent && parent.isG() && parent.parent() && parent.parent().is("cell"))
70     {
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();
75       assert(table);
76       advance(table);
77     }
78   else if (parent && parent.isG() && !parent.hasId() && parent.parent())
79     {
80       // closing brace for a right-open macro (like \over)
81       cursor.remove();
82       advance(parent.parent());
83     }
84   else
85     {
86       // ???
87       assert(0);
88     }
89 }
90
91 void
92 TPushParser::do_shift()
93 {
94   TNode parent = cursor.parent();
95   assert(parent);
96   if (parent.is("tex"))
97     {
98       TNode math = doc.create("math", nextId++);
99       TNode g = doc.createG();
100       cursor.replace(math);
101       math.append(g);
102       g.append(cursor);
103     }
104   else if (parent.isG() && !parent.hasId() && parent.parent() && parent.parent().is("math"))
105     {
106       if (cursor.prev())
107         {
108           // there is something before the cursor, hence this is the
109           // closing math shift
110           if (parent.parent()["display"] != "1")
111             {
112               // one math shift is enough to close it
113               cursor.remove();
114             }
115           else
116             {
117               // we need two closing math shifts
118               parent.parent().append(cursor);
119             }
120         }
121       else if (parent.parent()["display"] != "1")
122         {
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";
126         }
127       else
128         {
129           parent.parent().append(cursor);
130         }
131     }
132   else if (parent.is("math"))
133     {
134       cursor.remove();
135     }
136   else
137     {
138       cerr << "ERROR: math shift" << endl;
139     }
140 }
141
142 void
143 TPushParser::do_align()
144 {
145   TNode parent = cursor.parent();
146   if (parent && parent.isG() && parent.hasId())
147     {
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();
152       row.append(cell);
153       cell.append(g);
154       g.append(parent.first(), cursor);
155     }
156   else if (parent && parent.isG() && parent.parent().is("cell"))
157     {
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();
167       cell.append(g);
168       g.append(cursor);
169     }
170   else
171     {
172       cerr << "alignment tab used outside matrix" << endl;
173     }
174 }
175
176 void
177 TPushParser::do_eol()
178 {
179   //if (cursor.parent()) cursor.remove();
180 }
181
182 void
183 TPushParser::do_parameter(const std::string& p)
184 {
185   // ???
186 }
187
188 void
189 TPushParser::do_subscript()
190 {
191   TNode parent = cursor.parent();
192   if (parent.isG())
193     {
194       TNode prev = cursor.prev();
195       if (!prev)
196         {
197           TNode elem = doc.create("sb", nextId++);
198           TNode g = doc.createG();
199           cursor.replace(elem);
200           elem.append(g);
201           elem.append(cursor);
202         }
203       else
204         {
205           TNode elem = doc.create("sb", nextId++);
206           prev.replace(elem);
207           elem.append(prev);
208           elem.append(cursor);
209         }
210     }
211   else if (parent.isSb() && cursor == parent[1])
212     {
213       if (parent["under"] == "1") cerr << "already under" << endl;
214       else parent["under"] = "1";
215     }
216 }
217
218 void
219 TPushParser::do_superscript()
220 {
221   TNode parent = cursor.parent();
222   if (parent.isG())
223     {
224       TNode prev = cursor.prev();
225       if (!prev)
226         {
227           TNode elem = doc.create("sp", nextId++);
228           TNode g = doc.createG();
229           cursor.replace(elem);
230           elem.append(g);
231           elem.append(cursor);
232         }
233       else
234         {
235           TNode elem = doc.create("sp", nextId++);
236           prev.replace(elem);
237           elem.append(prev);
238           elem.append(cursor);
239         }
240     }
241   else if (parent.isSp() && cursor == parent[1])
242     {
243       if (parent["over"] == "1") cerr << "already over" << endl;
244       else parent["over"] = "1";
245     }
246 }
247
248 void
249 TPushParser::do_space(const std::string&)
250 {
251   // ? may be used to distinguish tokens in some mode?
252 }
253
254 void
255 TPushParser::do_letter(const std::string& s)
256 {
257   TNode parent = cursor.parent();
258   TNode elem = doc.createI(s, nextId++);
259   cursor.replace(elem);
260   advance(elem);
261 }
262
263 void
264 TPushParser::do_digit(const std::string& s)
265 {
266   TNode parent = cursor.parent();
267   TNode prev = cursor.prev();
268   if (prev && parent.isG() && prev.is("n"))
269     {
270       TNode elem = doc.createN(prev.value() + s, nextId++);
271       prev.replace(elem);
272     }
273   else
274     {
275       TNode elem = doc.createN(s, nextId++);
276       cursor.replace(elem);
277       advance(elem);
278     }
279 }
280
281 bool
282 TPushParser::isPrimes(const TNode& node) const
283 {
284   assert(node);
285   return node.isG() && node.last() && node.last().is("o") && node.last()["val"] == PRIME();
286 }
287
288 void
289 TPushParser::do_apostrophe()
290 {
291   if (cursor.parent() && cursor.parent().isG())
292     {
293       if (TNode prev = cursor.prev())
294         {
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++));
301           else
302             {
303               TNode elem = doc.create("sp");
304               TNode g = doc.createG();
305               prev.replace(elem);
306               elem.append(prev);
307               elem.append(g);
308               g.append(doc.createO(PRIME(), nextId++));
309             }
310         }
311       else
312         {
313           // error ???
314         }
315     }
316   else
317     {
318       // error ??
319     }
320 }
321
322 void
323 TPushParser::do_other(const std::string& s)
324 {
325   switch (s[0])
326     {
327     case '\'':
328       do_apostrophe();
329       break;
330     default:
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);
335       advance(elem);  
336       break;
337     }
338 }
339
340 void
341 TPushParser::do_active(const std::string&)
342 {
343   // ??? space?
344 }
345
346 void
347 TPushParser::do_comment()
348 {
349   // ???
350 }
351
352 void
353 TPushParser::do_cr()
354 {
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"))
359     {
360       TNode oldRow = parent.parent().parent();
361       assert(oldRow);
362       TNode table = oldRow.parent();
363       assert(table);
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);
369       row.append(cell);
370       cell.append(g);
371       g.append(cursor);
372     }
373 }
374
375 void
376 TPushParser::do_control(const std::string& name)
377 {
378   if (name == "cr") do_cr();
379   else
380     {
381       TNode parent = cursor.parent();
382       const TDictionary::Entry& entry = dictionary.find(name);
383       switch (entry.cls)
384         {
385         case TDictionary::IDENTIFIER:
386           {
387             TNode t = doc.createI(entry.value, nextId++);
388             t["name"] = name;
389             cursor.replace(t);
390             advance(t);
391           }
392           break;
393         case TDictionary::OPERATOR:
394           {
395             TNode t = doc.createO(entry.value, nextId++);
396             t["name"] = name;
397             cursor.replace(t);
398             advance(t);
399           }
400           break;
401         case TDictionary::NUMBER:
402           {
403             TNode t = doc.createN(entry.value, nextId++);
404             t["name"] = name;
405             cursor.replace(t);
406             advance(t);
407           }
408           break;
409         case TDictionary::MACRO:
410           {
411             TNode m = doc.createC(name, nextId++);
412             cursor.replace(m);
413             if (entry.leftOpen && entry.rightOpen)
414               {
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);
420                 m.append(g1);
421                 TNode g2 = doc.createG();
422                 g2.append(cursor);
423                 m.append(g2);
424                 frames.push(Frame(entry));
425               }
426             else if (entry.leftOpen)
427               {
428                 assert(parent.isG());
429                 TNode g = doc.createG();
430                 g["left-open"] = "1";
431                 g.append(parent.first(), m);
432                 m.append(g);
433                 advance(m);
434               }
435             else if (entry.rightOpen)
436               {
437                 assert(entry.pattern.empty());
438                 assert(parent.isG());
439                 TNode g = doc.createG();
440                 g.append(cursor);
441                 m.append(g);
442                 frames.push(Frame(entry));
443               }
444             else if (!entry.pattern.empty())
445               {
446                 if (parent.isG())
447                   {
448                     frames.push(Frame(entry));
449                     if (entry.paramDelimited(0))
450                       {
451                         TNode g = doc.createG();
452                         m.append(g);
453                         g.append(cursor);
454                       }
455                     else
456                       m.append(cursor);
457                   }
458                 else
459                   {
460                     // error, but we could handle this very easily
461                     cerr << "error, but we could handle this easily" << endl;
462                   }
463               }
464             else advance(m);
465           }
466           break;
467         case TDictionary::UNDEFINED:
468           {
469             cerr << "ERROR: using undefined macro `" << name << "'" << endl;
470             TNode m = doc.createC(name, nextId++);
471             cursor.replace(m);
472             advance(m);
473           }
474           break;
475         default:
476           assert(0);
477         }
478     }
479 }
480
481 void
482 TPushParser::process(const TToken& token)
483 {
484   switch (token.category)
485     {
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;
501     }
502 }
503
504 void
505 TPushParser::push(const TToken& token)
506 {
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
511   if (parent)
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())
517       {
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())
522           {
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)
529               {
530                 // The token matches with the delimiter of the argument.
531                 cursor.remove();
532
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
536                 // leave it alone
537                 // if (parent.size() == 1) parent.replace(parent[0]);
538
539                 // Eat both the argument and its delimiter
540                 frame.pos += 2;
541                 if (frame.pos == frame.entry.pattern.size())
542                   {
543                     // This token has completed the entry
544                     advance(parent.parent());
545                   }
546                 else if (frame.entry.paramDelimited(frame.pos))
547                   {
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);
552                     g.append(cursor);
553                   }
554                 else
555                   parent.parent().append(cursor);
556               }
557             else
558               {
559                 // Delimiter mismatch. Since we are parsing a delimited
560                 // argument we just process the token
561                 process(token);
562               }
563           }
564         else
565           {
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
569             // We'll see...
570             assert(frame.entry.rightOpen);
571             process(token);
572           }
573       }
574     else if (parent.isC())
575       {
576         // We are parsing a non-delimited argument entry
577         // or a fixed token
578         Frame& frame = frames.top();
579         assert(frame.pos < frame.entry.pattern.size());
580         if (frame.entry.pattern[frame.pos].category == TToken::PARAMETER)
581           {
582             // As by the TeX parsing rules of undelimited parameters,
583             // empty spaces are ignored
584             if (token.category != TToken::SPACE)
585               {
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
591                 frame.pos++;
592                 process(token);
593               }
594           }
595         else if (frame.entry.pattern[frame.pos] == token)
596           {
597             // The token has been accepted
598             frame.pos++;
599             if (frame.pos < frame.entry.pattern.size() &&
600                 frame.entry.paramDelimited(frame.pos))
601               {
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();
605                 cursor.replace(g);
606                 g.append(cursor);
607               }
608             else
609               advance(parent);
610           }
611         else
612           {
613             // There is a mismatch. Emit an error and ignore the token?
614             cerr << "ERROR: token ignored: " << token.value << " (cat: " << token.category << ")" << endl;
615           }
616
617       }
618     else
619       process(token);
620   else
621     {
622       cout << "ignored token" << endl;
623     }
624
625   if (listener) listener->callback(doc);
626 }
627
628 void
629 TPushParser::advance(const TNode& node)
630 {
631   assert(node);
632   TNode parent = node.parent();
633   if (!parent)
634     ; // nothing to do, the cursor is not in the document any more
635   else if (parent.isG())
636     {
637       TNode next = node.next();
638       if (next) next.insert(cursor);
639       else parent.append(cursor);
640     }
641   else if (parent.isC())
642     {
643       if (node.next())
644         ; // cursor removed
645       else
646         {
647           Frame& frame = frames.top();
648           if (frame.pos == frame.entry.pattern.size())
649             {
650               frames.pop();
651               advance(parent);
652             }
653           else
654             parent.append(cursor);
655         }
656     }
657   else if (parent.is("math"))
658     ; // we are done
659   else
660     advance(parent);
661 }
662
663 void
664 TPushParser::setCursor(const std::string& c)
665 {
666   cursor["val"] = c;
667   if (listener) listener->callback(doc);
668 }