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]
5 // Copyright 1996 Lyx Team.
13 #pragma implementation "lyxlex.h"
18 #include "support/filetools.h"
19 #include "support/lyxalgo.h"
22 using std::lower_bound;
29 // used by lower_bound
31 int operator()(keyword_item const & a, char const * const tag) const {
32 return compare_no_case(a.tag, tag) < 0;
34 // used by sorted and sort
36 int operator()(keyword_item const & a, keyword_item const & b) const {
37 return compare_no_case(a.tag, b.tag) < 0;
40 // } // end of anon namespace
43 LyXLex::LyXLex(keyword_item * tab, int num)
44 : is(&fb__), table(tab), no_items(num)
48 // Check if the table is sorted and if not, sort it.
49 if (table && !sorted(table, table + no_items, compare_tags())) {
50 lyxerr << "The table passed to LyXLex is not sorted!!\n"
51 << "Tell the developers to fix it!" << endl;
52 // We sort it anyway to avoid problems.
53 lyxerr << "\nUnsorted:\n";
56 sort(table, table + no_items,
58 lyxerr << "\nSorted:\n";
64 void LyXLex::pushTable(keyword_item * tab, int num)
66 pushed_table * tmppu = new pushed_table;
68 tmppu->table_elem = table;
69 tmppu->table_siz = no_items;
73 // Check if the table is sorted and if not, sort it.
74 if (table && !sorted(table, table + no_items, compare_tags())) {
75 lyxerr << "The table passed to LyXLex is not sorted!!\n"
76 << "Tell the developers to fix it!" << endl;
77 // We sort it anyway to avoid problems.
78 lyxerr << "\nUnsorted:\n";
81 sort(table, table + no_items, compare_tags());
82 lyxerr << "\nSorted:\n";
88 void LyXLex::popTable()
91 lyxerr << "LyXLex error: nothing to pop!" << endl;
95 table = tmp->table_elem;
96 no_items = tmp->table_siz;
103 void LyXLex::printTable(ostream & os)
105 os << "\nNumber of tags: " << no_items << '\n';
106 for(int i= 0; i < no_items; ++i)
108 << "]: tag: `" << table[i].tag
109 << "' code:" << table[i].code << '\n';
114 void LyXLex::printError(string const & message) const
116 string tmpmsg = subst(message, "$$Token", GetString());
117 lyxerr << "LyX: " << tmpmsg << " [around line " << lineno
118 << " of file " << MakeDisplayPath(name) << ']' << endl;
122 bool LyXLex::setFile(string const & filename)
125 lyxerr << "Error in LyXLex::setFile: "
126 "file or stream already set." << endl;
127 fb__.open(filename.c_str(), ios::in);
131 return fb__.is_open() && is.good();
135 void LyXLex::setStream(istream & i)
137 if (fb__.is_open() || is.rdbuf()->in_avail())
138 lyxerr << "Error in LyXLex::setStream: "
139 "file or stream already set." << endl;
147 //NOTE: possible bug.
148 if (next() && status == LEX_TOKEN)
149 return search_kw(buff);
155 int LyXLex::GetInteger() const
160 printError("Bad integer `$$Token'");
166 float LyXLex::GetFloat() const
171 printError("Bad float `$$Token'");
177 string LyXLex::GetString() const
183 // I would prefer to give a tag number instead of an explicit token
184 // here, but it is not possible because Buffer::readLyXformat2 uses
185 // explicit tokens (JMarc)
186 string LyXLex::getLongString(string const & endtoken)
189 bool firstline = true;
193 // blank line in the file being read
196 string const token = frontStrip(strip(GetString()), " \t");
198 lyxerr[Debug::PARSER] << "LongString: `"
199 << GetString() << '\'' << endl;
201 // We do a case independent comparison, like search_kw
203 if (compare_no_case(token, endtoken) != 0) {
204 string tmpstr = GetString();
207 while(i < tmpstr.length()
208 && tmpstr[i] == ' ') {
213 lyxerr[Debug::PARSER] << "Prefix = `" << prefix
218 && prefixIs(tmpstr, prefix.c_str())) {
219 tmpstr.erase(0, prefix.length() - 1);
221 str += tmpstr + '\n';
223 else // token == endtoken
227 printError("Long string not ended by `" + endtoken + '\'');
233 bool LyXLex::GetBool() const
235 if (compare(buff, "true") == 0)
237 else if (compare(buff, "false") != 0)
238 printError("Bad boolean `$$Token'. Use \"false\" or \"true\"");
243 bool LyXLex::EatLine()
246 unsigned char c = '\0';
248 while(is && c != '\n' && i != (LEX_MAX_BUFF - 1)) {
251 lyxerr[Debug::LYXLEX] << "LyXLex::EatLine read char: `"
256 if (i == (LEX_MAX_BUFF - 1) && c != '\n') {
257 printError("Line too long");
258 c = '\n'; // Pretend we had an end of line
259 --lineno; // but don't increase line counter (netto effect)
260 ++i; // and preserve last character read.
264 buff[--i] = '\0'; // i can never be 0 here, so no danger
274 int LyXLex::search_kw(char const * const tag) const
277 lower_bound(table, table + no_items, tag, compare_tags());
278 if (res != table + no_items && !compare_no_case(res->tag, tag))
284 bool LyXLex::next(bool esc)
287 unsigned char c = 0; // getc() returns an int
290 while (is && !status) {
294 // Read rest of line (fast :-)
295 is.getline(buff, sizeof(buff));
296 lyxerr[Debug::LYXLEX] << "Comment read: `" << c
297 << buff << "'" << endl;
309 } while (c != '\"' && c != '\n' && is &&
310 i != (LEX_MAX_BUFF - 2));
312 if (i == (LEX_MAX_BUFF - 2)) {
313 printError("Line too long");
314 c = '\"'; // Pretend we got a "
319 printError("Missing quote");
330 continue; /* Skip ','s */
332 // using relational operators with chars other
333 // than == and != is not safe. And if it is done
334 // the type _have_ to be unsigned. It usually a
335 // lot better to use the functions from cctype
342 } while (c > ' ' && c != ',' && is
343 && (i != LEX_MAX_BUFF - 1) );
344 if (i == LEX_MAX_BUFF - 1) {
345 printError("Line too long");
351 if (c == '\r' && is) {
352 // The Windows support has lead to the
353 // possibility of "\r\n" at the end of
354 // a line. This will stop LyX choking
355 // when it expected to find a '\n'
364 if (status) return true;
366 status = is.eof() ? LEX_FEOF: LEX_UNDEF;
370 unsigned char c = 0; // getc() returns an int
374 while (is && !status) {
379 if (c == ',') continue;
386 // escape the next char
393 } while (c > ' ' && c != ',' && is
394 && (i != LEX_MAX_BUFF - 1) );
395 if (i == LEX_MAX_BUFF - 1) {
396 printError("Line too long");
404 // Read rest of line (fast :-)
405 is.getline(buff, sizeof(buff));
406 lyxerr[Debug::LYXLEX] << "Comment read: `" << c
407 << buff << "'" << endl;
415 bool escaped = false;
420 if (c == '\r') continue;
422 // escape the next char
429 if (!escaped && c == '\"') break;
430 } while (c != '\n' && is &&
431 i != (LEX_MAX_BUFF - 2));
433 if (i == (LEX_MAX_BUFF - 2)) {
434 printError("Line too long");
435 c = '\"'; // Pretend we got a "
440 printError("Missing quote");
454 // escape the next char
462 } while (c > ' ' && c != ',' && is
463 && (i != LEX_MAX_BUFF-1) );
464 if (i == LEX_MAX_BUFF-1) {
465 printError("Line too long");
475 if (status) return true;
477 status = is.eof() ? LEX_FEOF : LEX_UNDEF;
484 bool LyXLex::nextToken()
487 while (is && !status) {
492 if (c >= ' ' && is) {
494 if (c == '\\') { // first char == '\\'
499 } while (c > ' ' && c != '\\' && is
500 && i != (LEX_MAX_BUFF-1));
506 } while (c >= ' ' && c != '\\' && is
507 && i != (LEX_MAX_BUFF-1));
510 if (i == (LEX_MAX_BUFF - 1)) {
511 printError("Line too long");
514 if (c == '\\') is.putback(c); // put it back
523 if (status) return true;
525 status = is.eof() ? LEX_FEOF: LEX_UNDEF;
531 int LyXLex::FindToken(char const * str[])
536 if (compare(buff, "default")) {
537 for (i = 0; str[i][0] && compare(str[i], buff); ++i);
539 printError("Unknown argument `$$Token'");
544 printError("file ended while scanning string token");
549 int LyXLex::CheckToken(char const * str[], int print_error)
553 if (compare(buff, "default")) {
554 for (i = 0; str[i][0] && compare(str[i], buff); ++i);
557 printError("Unknown argument `$$Token'");