]> matita.cs.unibo.it Git - helm.git/blob - helm/DEVEL/mathml_editor/src/TDictionary.cc
Added the completion of the macro's name.
[helm.git] / helm / DEVEL / mathml_editor / src / TDictionary.cc
1 /* This file is part of EdiTeX, an editor of mathematical
2  * expressions based on TeX syntax.
3  * 
4  * Copyright (C) 2002-2003 Luca Padovani <lpadovan@cs.unibo.it>,
5  *                    2003 Paolo Marinelli <pmarinel@cs.unibo.it>.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * For more information, please visit the project's home page
22  * http://helm.cs.unibo.it/editex/
23  * or send an email to <lpadovan@cs.unibo.it>
24  */
25
26 #include <sstream>
27
28 #include "dom.hh"
29 #include "TDictionary.hh"
30 #include "TTokenizer.hh"
31 #include "CLoggerConsole.hh"
32
33 static TDictionary::Entry undefinedEntry;
34
35 static std::string
36 getURIBase(const std::string& uri)
37 {
38   std::string::size_type slash = uri.rfind('/');
39   if (slash != std::string::npos) return uri.substr(0, slash + 1);
40   else return "";
41 }
42
43 static std::string
44 getURIName(const std::string& uri)
45 {
46   std::string::size_type slash = uri.rfind('/');
47   if (slash != std::string::npos) return uri.substr(slash + 1, uri.size());
48   else return uri;
49 }
50
51 void
52 TDictionary::load(const std::string& uri)
53 {
54   load(getURIName(uri), getURIBase(uri));
55 }
56
57 void
58 TDictionary::load(const std::string& name, const std::string& base)
59 {
60   logger.debug("Dictionary: loading `" + base + name + "'");
61
62   DOM::DOMImplementation di;
63   DOM::Document doc = di.createDocumentFromURI((base + name).c_str());
64   assert(doc);
65   load(doc, base);
66 }
67
68 void
69 TDictionary::load(const DOM::Document& doc, const std::string& base)
70 {
71   assert(doc);
72
73   DOM::Element root = doc.get_documentElement();
74   assert(root);
75
76   CLoggerConsole logger;
77   TTokenizer tokenizer(logger);
78
79   for (DOM::Node p = root.get_firstChild(); p; p = p.get_nextSibling())
80     if (p.get_nodeType() == DOM::Node::ELEMENT_NODE && p.get_nodeName() == "include")
81       {
82         DOM::Element el = p;
83         assert(el);
84         if (el.hasAttribute("href"))
85           {
86             // WARNING: this may result into an infinite loop!
87             std::string href = el.getAttribute("href");
88             std::string newBase = getURIBase(href);
89             std::string newName = getURIName(href);
90             if (newBase != "") load(newName, newBase);
91             else load(newName, base);
92           }
93         else
94           logger.warning("Dictionary: include statement with no href attribute (ignored)");
95       }
96     else if (p.get_nodeType() == DOM::Node::ELEMENT_NODE && p.get_nodeName() == "entry")
97       {
98         DOM::Element el = p;
99         assert(el);
100         assert(el.hasAttribute("name"));
101
102         std::string name = el.getAttribute("name");
103         if (entries.find(name) != entries.end())
104           logger.info("Dictionary: `" + name + "' is being redefined");
105
106         Entry entry;
107
108         if (el.hasAttribute("class"))
109           {
110             std::string cls = el.getAttribute("class");
111             if (cls == "o") entry.cls = OPERATOR;
112             else if (cls == "i") entry.cls = IDENTIFIER;
113             else if (cls == "n") entry.cls == NUMBER;
114             else entry.cls = MACRO;
115           }
116         else
117           entry.cls = MACRO;
118
119         if (el.hasAttribute("val"))
120           {
121             entry.value = el.getAttribute("val");
122             if (entry.cls == MACRO)
123               logger.warning("Dictionary: `" + name + "' has a specified value, but is classified as macro");
124           }
125
126         if (el.hasAttribute("pattern"))
127           {
128             if (entry.cls != MACRO)
129               logger.warning("Dictionary: `" + name + "' has a specified pattern, but is not classified as macro");
130
131             std::string pattern = el.getAttribute("pattern");
132             if (pattern == "{}")
133               entry.leftOpen = entry.rightOpen = 1;
134             else if (pattern == "{")
135               entry.leftOpen = 1;
136             else if (pattern == "}")
137               entry.rightOpen = 1;
138             else
139               entry.pattern = tokenizer.tokenize(pattern);
140           }
141
142 #if 0
143         if (el.hasAttribute("infix"))
144           {
145             std::istringstream is(el.getAttribute("infix"));
146             unsigned infix;
147             is >> infix;
148             entry.infix = infix;
149             if (!el.hasAttribute("prefix")) entry.prefix = infix;
150             if (!el.hasAttribute("postfix")) entry.postfix = infix;
151           }
152
153         if (el.hasAttribute("prefix"))
154           {
155             std::istringstream is(el.getAttribute("prefix"));
156             unsigned prefix;
157             is >> prefix;
158             entry.prefix = prefix;
159             if (!el.hasAttribute("infix"))
160               {
161                 entry.infix = prefix;
162                 if (!el.hasAttribute("postfix")) entry.postfix = prefix;
163               }
164           }
165
166         if (el.hasAttribute("postfix"))
167           {
168             std::istringstream is(el.getAttribute("postfix"));
169             unsigned postfix;
170             is >> postfix;
171             entry.postfix = postfix;
172             if (!el.hasAttribute("infix"))
173               {
174                 entry.infix = postfix;
175                 if (!el.hasAttribute("prefix")) entry.prefix = postfix;
176               }
177           }
178 #endif
179
180         if (el.hasAttribute("limits"))
181           {
182             std::istringstream is(el.getAttribute("limits"));
183             unsigned limits;
184             is >> limits;
185             entry.limits = limits;
186           }
187
188         if (el.hasAttribute("embellishment"))
189           {
190             std::istringstream is(el.getAttribute("embellishment"));
191             unsigned embellishment;
192             is >> embellishment;
193             entry.embellishment = embellishment;
194           }
195
196         if (el.hasAttribute("delimiter"))
197           {
198             if (entry.cls != OPERATOR && !entry.embellishment)
199               logger.warning("Dictionary: `" + name + "' delimiter ignored for non-operator");
200
201             std::istringstream is(el.getAttribute("delimiter"));
202             unsigned delimiter;
203             is >> delimiter;
204             entry.delimiter = delimiter;
205           }
206
207         if (el.hasAttribute("table"))
208           {
209             if (entry.cls != MACRO)
210               logger.warning("Dictionary: `" + name + "' table ignored for non-macro");
211
212             std::istringstream is(el.getAttribute("table"));
213             unsigned table;
214             is >> table;
215             entry.table = table;
216           }
217
218         entries[name] = entry;
219       }
220 }
221
222 const TDictionary::Entry&
223 TDictionary::find(const std::string& name) const
224 {
225   Dictionary::const_iterator p = entries.find(name);
226   if (p != entries.end()) return (*p).second;
227   else
228     {
229       logger.warning("unknown entry `" + name + "'");
230       return undefinedEntry;
231     }
232 }
233
234 std::string
235 TDictionary::complete(const std::string prefix, std::list<std::string>& complete_list) const
236 {
237   bool no_match = true;
238   std::string new_prefix = "";
239   for (Dictionary::const_iterator i = entries.begin(); i != entries.end(); i++)
240     {
241       if ((*i).first.find(prefix) == 0)
242         {
243           complete_list.push_front((*i).first);
244           if (no_match)
245             {
246               // it's the first match
247               no_match = false;
248               new_prefix = (*i).first;
249             }
250           else
251             {
252               // in this case, new_prefix has been set yet.
253               std::string s1 = (*i).first.substr(prefix.length()); // s1 is the high part of the matching string
254               std::string s2 = new_prefix.substr(prefix.length()); // s2 is the high part of new_prefix
255 #if 0
256               long j = 0; // it's the number of common characters
257               while (s1[j] == s2[j]) j++;
258 #endif
259               std::string::const_iterator i1 = s1.begin();
260               std::string::const_iterator i2 = s2.begin();
261               while (i1 != s1.end() && i2 != s2.end() && *i1 == *i2) i1++, i2++;
262               new_prefix = prefix + s1.substr(0, i1 - s1.begin());
263               //new_prefix = (j) ? prefix + s1.substr(0, i1 - s1.begin()) : prefix;
264             }
265         }
266     }
267
268   return new_prefix;
269 }
270
271 bool
272 TDictionary::Entry::paramDelimited(unsigned i) const
273 {
274   assert(i < pattern.size());
275   assert(pattern[i].category == TToken::PARAMETER);
276   // a parameter is delimited if it is NOT the last one
277   // AND the next argument is not a parameter
278   return i + 1 < pattern.size() && pattern[i + 1].category != TToken::PARAMETER;
279 }
280
281 bool
282 TDictionary::Entry::lastDelimiter(unsigned i) const
283 {
284   assert(i < pattern.size());
285   assert(pattern[i].category != TToken::PARAMETER);
286   // a token is the last delimiter if it is the last token 
287   // of the pattern or if the next token is a parameter)
288   return i + 1 == pattern.size() || pattern[i + 1].category == TToken::PARAMETER;
289 }
290
291 unsigned
292 TDictionary::Entry::previousParam(unsigned i) const
293 {
294   // this method return the position in the pattern of the 
295   // parameter placed in a position preceding i.
296   // If no preceding i parameter present, the method return
297   // pattern.size().
298   // To know the position of the last parameter, call this 
299   // method with i == pattern.size()
300   unsigned j = i - 1;
301
302   while (pattern[j].category != TToken::PARAMETER)
303     {
304       if (j) j--;
305       else return pattern.size();
306     }
307   return j;
308 }