]> git.lyx.org Git - lyx.git/blob - src/lyxlex.C
f036360a67669472561ff581c5ff8a9d95bc2d70
[lyx.git] / src / lyxlex.C
1 //  Generalized simple lexical analizer.
2 //  It can be used for simple syntax parsers, like lyxrc,
3 //  texclass and others to come.   [asierra30/03/96]
4 //
5 //   (C) 1996 Lyx Team.
6
7 #include <config.h>
8
9 #include <cstdlib>
10
11 #ifdef __GNUG__
12 #pragma implementation "lyxlex.h"
13 #endif
14
15 #include "lyxlex.h"
16 #include "error.h"
17 #include "support/filetools.h"
18
19 LyXLex::LyXLex(keyword_item * tab, int num)
20         : table(tab), no_items(num)
21 {
22         file = 0;
23         owns_file = false;
24         status = 0;
25         pushed = 0;
26 }
27
28
29 void LyXLex::pushTable(keyword_item * tab, int num)
30 {
31         pushed_table * tmppu = new pushed_table;
32         tmppu->next = pushed;
33         tmppu->table_elem = table;
34         tmppu->table_siz = no_items;
35         pushed = tmppu;
36         table = tab;
37         no_items = num;
38 }
39
40
41 void LyXLex::popTable()
42 {
43         if (pushed == 0)
44                 lyxerr.print("LyXLex error: nothing to pop!");
45
46         pushed_table * tmp;
47         tmp = pushed;
48         table = tmp->table_elem;
49         no_items = tmp->table_siz;
50         tmp->table_elem = 0;
51         pushed = tmp->next;
52         delete tmp;
53 }
54
55
56 void LyXLex::printTable()
57 {
58         lyxerr.print(string("\nNumber of tags: ") + tostr(no_items));
59         for(int i=0; i<no_items; i++)
60                 lyxerr.print(string("table[")+ tostr(i) +
61                               "]:  tag: `" + table[i].tag +
62                               "'  code:" + tostr(table[i].code));
63         lyxerr.print(string());
64 }
65
66
67 void LyXLex::printError(string const & message)
68 {
69         string tmpmsg = subst(message, "$$Token", GetString());
70         lyxerr.print("LyX: " + tmpmsg + " [around line " + tostr(lineno) + " of file "
71                       + MakeDisplayPath(name) + ']');
72 }
73
74
75 bool LyXLex::setFile(string const & filename)
76 {
77         if (file) 
78                 lyxerr.print("Error in LyXLex::setFile: file already set.");
79         file = fopen(filename.c_str(), "r");
80         name = filename;
81         owns_file = true;
82         lineno = 0;
83         return (file ? true : false);
84 }
85
86
87 void LyXLex::setFile(FILE * f)
88 {
89         if (file) 
90                 lyxerr.print("Error in LyXLex::setFile: file already set.");
91         file = f;
92         owns_file = false;
93         lineno = 0; // this is bogus if the file already has been read from
94 }
95
96
97 int LyXLex::lex()
98 {
99         //NOTE: possible bug.
100    if (next() && status==LEX_TOKEN)
101        return search_kw(buff);
102    else
103        return status;
104 }
105
106
107 int LyXLex::GetInteger()
108 {
109    if (buff[0]>' ')   
110        return atoi(buff);
111    else {
112         printError("Bad integer `$$Token'");
113         return -1;
114    }
115 }
116
117
118 float LyXLex::GetFloat()
119 {
120    if (buff[0]>' ')   
121        return (float)strtod(buff, (char**)0);
122    else {
123         printError("Bad float `$$Token'");
124         return -1;
125    }
126 }
127
128
129 string LyXLex::GetString() const
130 {
131         return string(buff);
132 }
133
134
135 // I would prefer to give a tag number instead of an explicit token
136 // here, but it is not possible because Buffer::readLyXformat2 uses
137 // explicit tokens (JMarc) 
138 string LyXLex::getLongString(string const & endtoken)
139 {
140         string str, prefix;
141         bool firstline = true;
142
143         while (IsOK()) {
144                 if (!EatLine())
145                         // blank line in the file being read
146                         continue;
147                 
148                 string const token = frontStrip(strip(GetString()), " \t");
149                 
150                 lyxerr.debug("LongString: `"+GetString()+'\'', Error::LEX_PARSER);
151
152                 // We do a case independent comparison, like search_kw
153                 // does.
154                 if (compare_no_case(token, endtoken) != 0) {
155                         string tmpstr = GetString();
156                         if (firstline) {
157                                 unsigned int i = 0;
158                                 while(i < tmpstr.length()
159                                       && tmpstr[i] == ' ') {
160                                         ++i;
161                                         prefix += ' ';
162                                 }
163                                 firstline = false;
164                                 lyxerr.debug("Prefix = `"+prefix+'\'',
165                                               Error::LEX_PARSER); 
166                         } 
167
168                         if (!prefix.empty() 
169                             && prefixIs(tmpstr, prefix.c_str())) {
170                                 tmpstr.erase(0, prefix.length() - 1);
171                         }
172                         str += tmpstr + '\n';
173                 }
174                 else // token == endtoken
175                         break;
176         }
177         if (!IsOK())
178                 printError("Long string not ended by `" + endtoken + '\'');
179
180         return str;
181 }
182
183
184 bool LyXLex::GetBool()
185 {
186    if (compare(buff, "true") == 0)
187         return true;
188    else if (compare(buff, "false") != 0)
189         printError("Bad boolean `$$Token'. Use \"false\" or \"true\"");
190    return false;
191 }
192
193
194 bool LyXLex::EatLine()
195 {
196         int i=0;
197         int c = '\0'; // getc() returns an int
198
199         while (!feof(file) && c!='\n' && i!=(LEX_MAX_BUFF-1)) {
200                 c = getc(file);
201                 if (c != '\r')
202                         buff[i++] = c;
203         }
204         if (i==(LEX_MAX_BUFF-1) && c !='\n') {
205                 printError("Line too long");
206                 c = '\n'; // Pretend we had an end of line
207                 --lineno; // but don't increase line counter (netto effect)
208                 ++i; // and preserve last character read.
209         }
210         if (c=='\n') {
211                 ++lineno;
212                 buff[--i] = '\0'; // i can never be 0 here, so no danger
213                 status = LEX_DATA;
214                 return true;
215         } else {
216                 buff[i] = '\0';
217                 return false;
218         }
219 }
220
221
222 int LyXLex::search_kw(char const * const tag) const
223 {
224         int m, k=0 , l= 0, r=no_items;
225
226         while (l < r) {
227                 m = (l+r)/2;
228
229                 if (lyxerr.debugging(Error::LEX_PARSER)) {
230                         string my_l;
231                         my_l+="LyXLex::search_kw: elem " ;
232                         my_l+= m; 
233                         my_l+=" tag "; 
234                         my_l+=table[m].tag;
235                         my_l+=" search tag ";
236                         my_l+= tag;
237                         lyxerr.print(my_l);
238                 }
239
240                 if (table[m].tag)
241                         k = compare_no_case(table[m].tag, tag);
242                 if (k==0)
243                         return table[m].code;
244                 else
245                         if (k<0) l = m+1; else r = m;
246         }
247         return -1;
248 }
249
250
251 bool LyXLex::next(bool esc)
252 {
253
254         if (!esc) {
255                 int c; // getc() returns an int
256                 int i;
257                 
258                 
259                 status = 0;
260                 while (!feof(file) && !status) { 
261                         c = getc(file);
262                         if (c=='#') {
263                                 // Read rest of line (fast :-)
264                                 fgets(buff, sizeof(buff), file);
265                                 ++lineno;
266                                 continue;
267                         }
268                         
269                         if (c=='\"') {
270                                 i = -1;
271                                 do {
272                                         c = getc(file);
273                                         if (c != '\r')
274                                                 buff[++i] = c;
275                                 } while (c!='\"' && c!='\n' && !feof(file) &&
276                                          i!=(LEX_MAX_BUFF-2));
277                                 
278                                 if (i==(LEX_MAX_BUFF-2)) {
279                                         printError("Line too long");
280                                         c = '\"'; // Pretend we got a "
281                                         ++i;
282                                 }
283                                 
284                                 if (c!='\"') {
285                                         printError("Missing quote");
286                                         if (c=='\n')
287                                                 ++lineno;
288                                 }
289                                 
290                                 buff[i] = '\0';
291                                 status = LEX_DATA;
292                                 break; 
293                         }
294                         
295                         if (c==',')
296                                 continue;              /* Skip ','s */
297                         
298                         if (c > ' ' && !feof(file))  {
299                                 i = 0;
300                                 do {
301                                         buff[i++] = c;
302                                         c = getc(file);
303                                 } while (c > ' ' && c != ',' && !feof(file) &&
304                                          (i != LEX_MAX_BUFF-1) );
305                                 if (i == LEX_MAX_BUFF-1) {
306                                         printError("Line too long");
307                                 }
308                                 buff[i] = '\0';
309                                 status = LEX_TOKEN;
310                         }
311                         
312                         if (c== '\r' && !feof(file)) {
313                                 // The Windows support has lead to the
314                                 // possibility of "\r\n" at the end of
315                                 // a line.  This will stop LyX choking
316                                 // when it expected to find a '\n'
317                                 c = getc(file);
318                         }
319
320                         if (c=='\n')
321                                 ++lineno;
322                         
323                 }
324                 if (status) return true;
325                 
326                 status = (feof(file)) ? LEX_FEOF: LEX_UNDEF;
327                 buff[0] = '\0';
328                 return false;
329         } else {
330                 int c; // getc() returns an int
331                 int i;
332                 
333                 
334                 status = 0;
335                 while (!feof(file) && !status) { 
336                         c = getc(file);
337
338                         // skip ','s
339                         if (c==',') continue;
340                         
341                         if (c=='\\') {
342                                 // escape
343                                 i = 0;
344                                 do {
345                                         if (c == '\\') {
346                                                 // escape the next char
347                                                 c = getc(file);
348                                         }
349                                         buff[i++] = c;
350                                         c = getc(file);
351                                 } while (c > ' ' && c != ',' && !feof(file) &&
352                                          (i != LEX_MAX_BUFF-1) );
353                                 if (i == LEX_MAX_BUFF-1) {
354                                         printError("Line too long");
355                                 }
356                                 buff[i] = '\0';
357                                 status = LEX_TOKEN;
358                                 continue;
359                         }
360                         
361                         if (c=='#') {
362                                 // Read rest of line (fast :-)
363                                 fgets(buff, sizeof(buff), file);
364                                 ++lineno;
365                                 continue;
366                         }
367
368                         // string
369                         if (c=='\"') {
370                                 i = -1;
371                                 bool escaped = false;
372                                 do {
373                                         escaped = false;
374                                         c = getc(file);
375                                         if (c == '\r') continue;
376                                         if (c == '\\') {
377                                                 // escape the next char
378                                                 c = getc(file);
379                                                 escaped = true;
380                                         }
381                                         buff[++i] = c;
382                                 
383                                         if (!escaped && c == '\"') break;
384                                 } while (c!='\n' && !feof(file) &&
385                                          i!=(LEX_MAX_BUFF-2));
386                                 
387                                 if (i==(LEX_MAX_BUFF-2)) {
388                                         printError("Line too long");
389                                         c = '\"'; // Pretend we got a "
390                                         ++i;
391                                 }
392                                 
393                                 if (c!='\"') {
394                                         printError("Missing quote");
395                                         if (c=='\n')
396                                                 ++lineno;
397                                 }
398                                 
399                                 buff[i] = '\0';
400                                 status = LEX_DATA;
401                                 break; 
402                         }
403                         
404                         if (c > ' ' && !feof(file))  {
405                                 i = 0;
406                                 do {
407                                         if (c == '\\') {
408                                                 // escape the next char
409                                                 c = getc(file);
410                                                 //escaped = true;
411                                         }
412                                         buff[i++] = c;
413                                         c = getc(file);
414                                 } while (c > ' ' && c != ',' && !feof(file) &&
415                                          (i != LEX_MAX_BUFF-1) );
416                                 if (i == LEX_MAX_BUFF-1) {
417                                         printError("Line too long");
418                                 }
419                                 buff[i] = '\0';
420                                 status = LEX_TOKEN;
421                         }
422
423                         // new line
424                         if (c=='\n')
425                                 ++lineno;
426                 }
427                 
428                 if (status) return true;
429                 
430                 status = (feof(file)) ? LEX_FEOF: LEX_UNDEF;
431                 buff[0] = '\0';
432                 return false;   
433         }
434 }
435
436
437 bool LyXLex::nextToken()
438 {
439         int c; // getc() returns an int
440         int i;
441         
442         status = 0;
443         while (!feof(file) && !status) { 
444                 c = getc(file);
445            
446                 if (c >= ' ' && !feof(file))  {
447                         i = 0;
448                         if (c == '\\') { // first char == '\\'
449                                 do {
450                                         buff[i++] = c;
451                                         c = getc(file);
452                                 } while (c > ' ' && c != '\\' && !feof(file) &&
453                                          i != (LEX_MAX_BUFF-1));
454                         } else {
455                                 do {
456                                         buff[i++] = c;
457                                         c = getc(file);
458                                 } while (c >= ' ' && c != '\\' && !feof(file)
459                                          && i != (LEX_MAX_BUFF-1));
460                         }
461
462                         if (i == (LEX_MAX_BUFF-1)) {
463                                 printError("Line too long");
464                         }
465
466                         if (c == '\\') ungetc(c,file); // put it back
467                         buff[i] = '\0';
468                         status = LEX_TOKEN;
469                 }
470                   
471                 if (c=='\n')
472                         ++lineno;
473         
474         }
475         if (status)  return true;
476         
477         status = (feof(file)) ? LEX_FEOF: LEX_UNDEF;
478         buff[0] = '\0';
479         return false;
480 }
481
482
483 int LyXLex::FindToken(char const * str[])
484 {  
485    int i = -1;
486    
487    if (next()) {
488       if (compare(buff, "default")) {
489          for (i = 0; str[i][0] && compare(str[i], buff); ++i);
490          if (!str[i][0]) {
491             printError("Unknown argument `$$Token'");
492             i = -1;
493          }
494       }  
495    } else
496      printError("file ended while scanning string token");
497    return i;
498 }
499
500
501 int LyXLex::CheckToken(char const * str[], int print_error)
502 {  
503    int i = -1;
504    
505    if (compare(buff, "default")) {
506        for (i = 0; str[i][0] && compare(str[i], buff); i++);
507        if (!str[i][0]) {
508            if (print_error)
509                printError("Unknown argument `$$Token'");
510            i = -1;
511        }
512    }
513    return i;
514 }