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"
24 using std::lower_bound;
31 // used by lower_bound
33 int operator()(keyword_item const & a, char const * const tag) const {
34 return compare_no_case(a.tag, tag) < 0;
36 // used by sorted and sort
38 int operator()(keyword_item const & a, keyword_item const & b) const {
39 return compare_no_case(a.tag, b.tag) < 0;
42 // } // end of anon namespace
45 LyXLex::LyXLex(keyword_item * tab, int num)
46 : is(&fb__), table(tab), no_items(num)
50 // Check if the table is sorted and if not, sort it.
51 if (table && !sorted(table, table + no_items, compare_tags())) {
52 lyxerr << "The table passed to LyXLex is not sorted!!\n"
53 << "Tell the developers to fix it!" << endl;
54 // We sort it anyway to avoid problems.
55 lyxerr << "\nUnsorted:\n";
58 sort(table, table + no_items,
60 lyxerr << "\nSorted:\n";
66 void LyXLex::pushTable(keyword_item * tab, int num)
68 pushed_table * tmppu = new pushed_table;
70 tmppu->table_elem = table;
71 tmppu->table_siz = no_items;
75 // Check if the table is sorted and if not, sort it.
76 if (table && !sorted(table, table + no_items, compare_tags())) {
77 lyxerr << "The table passed to LyXLex is not sorted!!\n"
78 << "Tell the developers to fix it!" << endl;
79 // We sort it anyway to avoid problems.
80 lyxerr << "\nUnsorted:\n";
83 sort(table, table + no_items, compare_tags());
84 lyxerr << "\nSorted:\n";
90 void LyXLex::popTable()
93 lyxerr << "LyXLex error: nothing to pop!" << endl;
97 table = tmp->table_elem;
98 no_items = tmp->table_siz;
105 void LyXLex::printTable(ostream & os)
107 os << "\nNumber of tags: " << no_items << '\n';
108 for(int i= 0; i < no_items; ++i)
110 << "]: tag: `" << table[i].tag
111 << "' code:" << table[i].code << '\n';
116 void LyXLex::printError(string const & message) const
118 string tmpmsg = subst(message, "$$Token", GetString());
119 lyxerr << "LyX: " << tmpmsg << " [around line " << lineno
120 << " of file " << MakeDisplayPath(name) << ']' << endl;
124 bool LyXLex::setFile(string const & filename)
127 lyxerr << "Error in LyXLex::setFile: "
128 "file or stream already set." << endl;
129 fb__.open(filename.c_str(), ios::in);
133 return fb__.is_open() && is.good();
137 void LyXLex::setStream(istream & i)
139 if (fb__.is_open() || is.rdbuf()->in_avail())
140 lyxerr << "Error in LyXLex::setStream: "
141 "file or stream already set." << endl;
149 //NOTE: possible bug.
150 if (next() && status == LEX_TOKEN)
151 return search_kw(buff);
157 int LyXLex::GetInteger() const
162 printError("Bad integer `$$Token'");
168 float LyXLex::GetFloat() const
173 printError("Bad float `$$Token'");
179 string LyXLex::GetString() const
185 // I would prefer to give a tag number instead of an explicit token
186 // here, but it is not possible because Buffer::readLyXformat2 uses
187 // explicit tokens (JMarc)
188 string LyXLex::getLongString(string const & endtoken)
191 bool firstline = true;
195 // blank line in the file being read
198 string const token = frontStrip(strip(GetString()), " \t");
200 lyxerr[Debug::PARSER] << "LongString: `"
201 << GetString() << '\'' << endl;
203 // We do a case independent comparison, like search_kw
205 if (compare_no_case(token, endtoken) != 0) {
206 string tmpstr = GetString();
209 while(i < tmpstr.length()
210 && tmpstr[i] == ' ') {
215 lyxerr[Debug::PARSER] << "Prefix = `" << prefix
220 && prefixIs(tmpstr, prefix.c_str())) {
221 tmpstr.erase(0, prefix.length() - 1);
223 str += tmpstr + '\n';
225 else // token == endtoken
229 printError("Long string not ended by `" + endtoken + '\'');
235 bool LyXLex::GetBool() const
237 if (compare(buff, "true") == 0)
239 else if (compare(buff, "false") != 0)
240 printError("Bad boolean `$$Token'. Use \"false\" or \"true\"");
245 bool LyXLex::EatLine()
248 unsigned char c = '\0';
250 while(is && c != '\n' && i != (LEX_MAX_BUFF - 1)) {
253 lyxerr[Debug::LYXLEX] << "LyXLex::EatLine read char: `"
258 if (i == (LEX_MAX_BUFF - 1) && c != '\n') {
259 printError("Line too long");
260 c = '\n'; // Pretend we had an end of line
261 --lineno; // but don't increase line counter (netto effect)
262 ++i; // and preserve last character read.
266 buff[--i] = '\0'; // i can never be 0 here, so no danger
276 int LyXLex::search_kw(char const * const tag) const
279 lower_bound(table, table + no_items, tag, compare_tags());
280 if (res != table + no_items && !compare_no_case(res->tag, tag))
286 bool LyXLex::next(bool esc)
289 unsigned char c = 0; // getc() returns an int
292 while (is && !status) {
296 // Read rest of line (fast :-)
297 is.getline(buff, sizeof(buff));
298 lyxerr[Debug::LYXLEX] << "Comment read: `" << c
299 << buff << "'" << endl;
311 } while (c != '\"' && c != '\n' && is &&
312 i != (LEX_MAX_BUFF - 2));
314 if (i == (LEX_MAX_BUFF - 2)) {
315 printError("Line too long");
316 c = '\"'; // Pretend we got a "
321 printError("Missing quote");
332 continue; /* Skip ','s */
334 // using relational operators with chars other
335 // than == and != is not safe. And if it is done
336 // the type _have_ to be unsigned. It usually a
337 // lot better to use the functions from cctype
344 } while (c > ' ' && c != ',' && is
345 && (i != LEX_MAX_BUFF - 1) );
346 if (i == LEX_MAX_BUFF - 1) {
347 printError("Line too long");
353 if (c == '\r' && is) {
354 // The Windows support has lead to the
355 // possibility of "\r\n" at the end of
356 // a line. This will stop LyX choking
357 // when it expected to find a '\n'
366 if (status) return true;
368 status = is.eof() ? LEX_FEOF: LEX_UNDEF;
372 unsigned char c = 0; // getc() returns an int
376 while (is && !status) {
381 if (c == ',') continue;
388 // escape the next char
395 } while (c > ' ' && c != ',' && is
396 && (i != LEX_MAX_BUFF - 1) );
397 if (i == LEX_MAX_BUFF - 1) {
398 printError("Line too long");
406 // Read rest of line (fast :-)
407 is.getline(buff, sizeof(buff));
408 lyxerr[Debug::LYXLEX] << "Comment read: `" << c
409 << buff << "'" << endl;
417 bool escaped = false;
422 if (c == '\r') continue;
424 // escape the next char
431 if (!escaped && c == '\"') break;
432 } while (c != '\n' && is &&
433 i != (LEX_MAX_BUFF - 2));
435 if (i == (LEX_MAX_BUFF - 2)) {
436 printError("Line too long");
437 c = '\"'; // Pretend we got a "
442 printError("Missing quote");
456 // escape the next char
464 } while (c > ' ' && c != ',' && is
465 && (i != LEX_MAX_BUFF-1) );
466 if (i == LEX_MAX_BUFF-1) {
467 printError("Line too long");
477 if (status) return true;
479 status = is.eof() ? LEX_FEOF : LEX_UNDEF;
486 bool LyXLex::nextToken()
489 while (is && !status) {
494 if (c >= ' ' && is) {
496 if (c == '\\') { // first char == '\\'
501 } while (c > ' ' && c != '\\' && is
502 && i != (LEX_MAX_BUFF-1));
508 } while (c >= ' ' && c != '\\' && is
509 && i != (LEX_MAX_BUFF-1));
512 if (i == (LEX_MAX_BUFF - 1)) {
513 printError("Line too long");
516 if (c == '\\') is.putback(c); // put it back
525 if (status) return true;
527 status = is.eof() ? LEX_FEOF: LEX_UNDEF;
533 int LyXLex::FindToken(char const * str[])
538 if (compare(buff, "default")) {
539 for (i = 0; str[i][0] && compare(str[i], buff); ++i);
541 printError("Unknown argument `$$Token'");
546 printError("file ended while scanning string token");
551 int LyXLex::CheckToken(char const * str[], int print_error)
555 if (compare(buff, "default")) {
556 for (i = 0; str[i][0] && compare(str[i], buff); ++i);
559 printError("Unknown argument `$$Token'");