]> git.lyx.org Git - lyx.git/blob - src/Lexer.cpp
Natbib authoryear uses (Ref1; Ref2) by default.
[lyx.git] / src / Lexer.cpp
1 /**
2  * \file Lexer.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alejandro Aguilar Sierra
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author John Levon
10  *
11  * Full author contact details are available in file CREDITS.
12  */
13
14 #include <config.h>
15
16 #include "Lexer.h"
17 #include "Format.h"
18
19 #include "support/convert.h"
20 #include "support/debug.h"
21 #include "support/FileName.h"
22 #include "support/filetools.h"
23 #include "support/gzstream.h"
24 #include "support/lassert.h"
25 #include "support/lstrings.h"
26 #include "support/lyxalgo.h"
27 #include "support/types.h"
28
29 #include <functional>
30 #include <istream>
31 #include <stack>
32 #include <vector>
33
34 using namespace std;
35 using namespace lyx::support;
36
37 namespace lyx {
38
39 //////////////////////////////////////////////////////////////////////
40 //
41 // Lexer::Pimpl
42 //
43 //////////////////////////////////////////////////////////////////////
44
45
46 ///
47 class Lexer::Pimpl {
48 public:
49         ///
50         Pimpl(LexerKeyword * tab, int num);
51         ///
52         string const getString() const;
53         ///
54         docstring const getDocString() const;
55         ///
56         void printError(string const & message) const;
57         ///
58         void printTable(ostream & os);
59         ///
60         void pushTable(LexerKeyword * tab, int num);
61         ///
62         void popTable();
63         ///
64         bool setFile(FileName const & filename);
65         ///
66         void setStream(istream & i);
67         ///
68         void setCommentChar(char c);
69         ///
70         bool next(bool esc = false);
71         ///
72         int searchKeyword(char const * const tag) const;
73         ///
74         int lex();
75         ///
76         bool eatLine();
77         ///
78         bool nextToken();
79         /// test if there is a pushed token or the stream is ok
80         bool inputAvailable();
81         ///
82         void pushToken(string const &);
83         /// fb_ is only used to open files, the stream is accessed through is.
84         filebuf fb_;
85
86         /// gz_ is only used to open files, the stream is accessed through is.
87         gz::gzstreambuf gz_;
88
89         /// the stream that we use.
90         istream is;
91         ///
92         string name;
93         ///
94         LexerKeyword * table;
95         ///
96         int no_items;
97         ///
98         string buff;
99         ///
100         int status;
101         ///
102         int lineno;
103         ///
104         string pushTok;
105         ///
106         char commentChar;
107         /// used for error messages
108         string context;
109 private:
110         /// non-copyable
111         Pimpl(Pimpl const &);
112         void operator=(Pimpl const &);
113
114         ///
115         void verifyTable();
116         ///
117         class PushedTable {
118         public:
119                 ///
120                 PushedTable()
121                         : table_elem(0), table_siz(0) {}
122                 ///
123                 PushedTable(LexerKeyword * ki, int siz)
124                         : table_elem(ki), table_siz(siz) {}
125                 ///
126                 LexerKeyword * table_elem;
127                 ///
128                 int table_siz;
129         };
130         ///
131         stack<PushedTable> pushed;
132 };
133
134
135
136 namespace {
137
138 class CompareTags
139         : public binary_function<LexerKeyword, LexerKeyword, bool> {
140 public:
141         // used by lower_bound, sort and sorted
142         bool operator()(LexerKeyword const & a, LexerKeyword const & b) const
143         {
144                 // we use the ascii version, because in turkish, 'i'
145                 // is not the lowercase version of 'I', and thus
146                 // turkish locale breaks parsing of tags.
147                 return compare_ascii_no_case(a.tag, b.tag) < 0;
148         }
149 };
150
151 } // end of anon namespace
152
153
154 Lexer::Pimpl::Pimpl(LexerKeyword * tab, int num)
155         : is(&fb_), table(tab), no_items(num),
156           status(0), lineno(0), commentChar('#')
157 {
158         verifyTable();
159 }
160
161
162 string const Lexer::Pimpl::getString() const
163 {
164         return buff;
165 }
166
167
168 docstring const Lexer::Pimpl::getDocString() const
169 {
170         return from_utf8(buff);
171 }
172
173
174 void Lexer::Pimpl::printError(string const & message) const
175 {
176         string const tmpmsg = subst(message, "$$Token", getString());
177         lyxerr << "LyX: " << tmpmsg << " [around line " << lineno
178                 << " of file " << to_utf8(makeDisplayPath(name))
179                 << " current token: '" << getString() << "'"
180                 << " context: '" << context << "']" << endl;
181 }
182
183
184 void Lexer::Pimpl::printTable(ostream & os)
185 {
186         os << "\nNumber of tags: " << no_items << endl;
187         for (int i= 0; i < no_items; ++i)
188                 os << "table[" << i
189                    << "]:  tag: `" << table[i].tag
190                    << "'  code:" << table[i].code << '\n';
191         os.flush();
192 }
193
194
195 void Lexer::Pimpl::verifyTable()
196 {
197         // Check if the table is sorted and if not, sort it.
198         if (table
199             && !lyx::sorted(table, table + no_items, CompareTags())) {
200                 lyxerr << "The table passed to Lexer is not sorted!\n"
201                        << "Tell the developers to fix it!" << endl;
202                 // We sort it anyway to avoid problems.
203                 lyxerr << "\nUnsorted:" << endl;
204                 printTable(lyxerr);
205
206                 sort(table, table + no_items, CompareTags());
207                 lyxerr << "\nSorted:" << endl;
208                 printTable(lyxerr);
209         }
210 }
211
212
213 void Lexer::Pimpl::pushTable(LexerKeyword * tab, int num)
214 {
215         PushedTable tmppu(table, no_items);
216         pushed.push(tmppu);
217
218         table = tab;
219         no_items = num;
220
221         verifyTable();
222 }
223
224
225 void Lexer::Pimpl::popTable()
226 {
227         if (pushed.empty()) {
228                 lyxerr << "Lexer error: nothing to pop!" << endl;
229                 return;
230         }
231
232         PushedTable tmp = pushed.top();
233         pushed.pop();
234         table = tmp.table_elem;
235         no_items = tmp.table_siz;
236 }
237
238
239 bool Lexer::Pimpl::setFile(FileName const & filename)
240 {
241 #ifdef TEX2LYX
242         // tex2lyx does not read lyxrc and therefore can't really check for
243         // zipped formats.
244         if (false) {
245 #else
246         // Check the format of the file.
247         if (formats.isZippedFile(filename)) {
248 #endif
249                 LYXERR(Debug::LYXLEX, "lyxlex: compressed");
250                 // The check only outputs a debug message, because it triggers
251                 // a bug in compaq cxx 6.2, where is_open() returns 'true' for
252                 // a fresh new filebuf.  (JMarc)
253                 if (gz_.is_open() || istream::off_type(is.tellg()) > -1)
254                         LYXERR(Debug::LYXLEX, "Error in LyXLex::setFile: "
255                                 "file or stream already set.");
256                 gz_.open(filename.toFilesystemEncoding().c_str(), ios::in);
257                 is.rdbuf(&gz_);
258                 name = filename.absFileName();
259                 lineno = 0;
260                 if (!gz_.is_open() || !is.good())
261                         return false;
262         } else {
263                 LYXERR(Debug::LYXLEX, "lyxlex: UNcompressed");
264
265                 // The check only outputs a debug message, because it triggers
266                 // a bug in compaq cxx 6.2, where is_open() returns 'true' for
267                 // a fresh new filebuf.  (JMarc)
268                 if (fb_.is_open() || istream::off_type(is.tellg()) > 0) {
269                         LYXERR(Debug::LYXLEX, "Error in Lexer::setFile: "
270                                 "file or stream already set.");
271                 }
272                 fb_.open(filename.toSafeFilesystemEncoding().c_str(), ios::in);
273                 is.rdbuf(&fb_);
274                 name = filename.absFileName();
275                 lineno = 0;
276                 if (!fb_.is_open() || !is.good())
277                         return false;
278         }
279
280         // Skip byte order mark.
281         if (is.peek() == 0xef) {
282                 is.get();
283                 if (is.peek() == 0xbb) {
284                         is.get();
285                         LASSERT(is.get() == 0xbf, /**/);
286                 } else
287                         is.unget();
288         }
289
290         return true;
291 }
292
293
294 void Lexer::Pimpl::setStream(istream & i)
295 {
296         if (fb_.is_open() || istream::off_type(is.tellg()) > 0) {
297                 LYXERR(Debug::LYXLEX, "Error in Lexer::setStream: "
298                         "file or stream already set.");
299         }
300         is.rdbuf(i.rdbuf());
301         lineno = 0;
302 }
303
304
305 void Lexer::Pimpl::setCommentChar(char c)
306 {
307         commentChar = c;
308 }
309
310
311 bool Lexer::Pimpl::next(bool esc /* = false */)
312 {
313         if (!pushTok.empty()) {
314                 // There can have been a whole line pushed so
315                 // we extract the first word and leaves the rest
316                 // in pushTok. (Lgb)
317                 if (pushTok[0] == '\\' && pushTok.find(' ') != string::npos) {
318                         buff.clear();
319                         pushTok = split(pushTok, buff, ' ');
320                 } else {
321                         buff = pushTok;
322                         pushTok.clear();
323                 }
324                 status = LEX_TOKEN;
325                 return true;
326         }
327
328
329         unsigned char c = 0; // getc() returns an int
330         char cc = 0;
331         status = 0;
332         while (is && !status) {
333                 is.get(cc);
334                 c = cc;
335
336                 if (c == commentChar) {
337                         // Read rest of line (fast :-)
338 #if 1
339                         // That is not fast... (Lgb)
340                         string dummy;
341                         getline(is, dummy);
342
343                         LYXERR(Debug::LYXLEX, "Comment read: `" << c << dummy << '\'');
344 #else
345                         // unfortunately ignore is buggy (Lgb)
346                         is.ignore(100, '\n');
347 #endif
348                         ++lineno;
349                         continue;
350                 }
351
352                 if (c == '\"') {
353                         buff.clear();
354
355                         if (esc) {
356
357                                 bool escaped = false;
358                                 do {
359                                         escaped = false;
360                                         is.get(cc);
361                                         c = cc;
362                                         if (c == '\r') continue;
363                                         if (c == '\\') {
364                                                 // escape the next char
365                                                 is.get(cc);
366                                                 c = cc;
367                                                 if (c == '\"' || c == '\\')
368                                                         escaped = true;
369                                                 else
370                                                         buff.push_back('\\');
371                                         }
372                                         buff.push_back(c);
373
374                                         if (!escaped && c == '\"')
375                                                 break;
376                                 } while (c != '\n' && is);
377
378                         } else {
379
380                                 do {
381                                         is.get(cc);
382                                         c = cc;
383                                         if (c != '\r')
384                                                 buff.push_back(c);
385                                 } while (c != '\"' && c != '\n' && is);
386
387                         }
388
389                         if (c != '\"') {
390                                 printError("Missing quote");
391                                 if (c == '\n')
392                                         ++lineno;
393                         }
394
395                         buff.resize(buff.size() - 1);
396                         status = LEX_DATA;
397                         break;
398                 }
399
400                 if (c == ',')
401                         continue;              /* Skip ','s */
402
403                 // using relational operators with chars other
404                 // than == and != is not safe. And if it is done
405                 // the type _have_ to be unsigned. It usually a
406                 // lot better to use the functions from cctype
407                 if (c > ' ' && is)  {
408                         buff.clear();
409
410                         do {
411                                 if (esc && c == '\\') {
412                                         // escape the next char
413                                         is.get(cc);
414                                         c = cc;
415                                         //escaped = true;
416                                 }
417                                 buff.push_back(c);
418                                 is.get(cc);
419                                 c = cc;
420                         } while (c > ' ' && c != ',' && is);
421                         status = LEX_TOKEN;
422                 }
423
424                 if (c == '\r' && is) {
425                         // The Windows support has lead to the
426                         // possibility of "\r\n" at the end of
427                         // a line.  This will stop LyX choking
428                         // when it expected to find a '\n'
429                         is.get(cc);
430                         c = cc;
431                 }
432
433                 if (c == '\n')
434                         ++lineno;
435
436         }
437         if (status)
438                 return true;
439
440         status = is.eof() ? LEX_FEOF: LEX_UNDEF;
441         buff.clear();
442         return false;
443 }
444
445
446 int Lexer::Pimpl::searchKeyword(char const * const tag) const
447 {
448         LexerKeyword search_tag = { tag, 0 };
449         LexerKeyword * res =
450                 lower_bound(table, table + no_items,
451                             search_tag, CompareTags());
452         // use the compare_ascii_no_case instead of compare_no_case,
453         // because in turkish, 'i' is not the lowercase version of 'I',
454         // and thus turkish locale breaks parsing of tags.
455         if (res != table + no_items
456             && !compare_ascii_no_case(res->tag, tag))
457                 return res->code;
458         return LEX_UNDEF;
459 }
460
461
462 int Lexer::Pimpl::lex()
463 {
464         //NOTE: possible bug.
465         if (next() && status == LEX_TOKEN)
466                 return searchKeyword(getString().c_str());
467         return status;
468 }
469
470
471 bool Lexer::Pimpl::eatLine()
472 {
473         buff.clear();
474
475         unsigned char c = '\0';
476         char cc = 0;
477         while (is && c != '\n') {
478                 is.get(cc);
479                 c = cc;
480                 //LYXERR(Debug::LYXLEX, "Lexer::EatLine read char: `" << c << '\'');
481                 if (c != '\r' && is)
482                         buff.push_back(c);
483         }
484
485         if (c == '\n') {
486                 ++lineno;
487                 buff.resize(buff.size() - 1);
488                 status = LEX_DATA;
489                 return true;
490         } else if (buff.length() > 0) { // last line
491                 status = LEX_DATA;
492                 return true;
493         } else {
494                 return false;
495         }
496 }
497
498
499 bool Lexer::Pimpl::nextToken()
500 {
501         if (!pushTok.empty()) {
502                 // There can have been a whole line pushed so
503                 // we extract the first word and leaves the rest
504                 // in pushTok. (Lgb)
505                 if (pushTok[0] == '\\' && pushTok.find(' ') != string::npos) {
506                         buff.clear();
507                         pushTok = split(pushTok, buff, ' ');
508                 } else {
509                         buff = pushTok;
510                         pushTok.clear();
511                 }
512                 status = LEX_TOKEN;
513                 return true;
514         }
515
516         status = 0;
517         while (is && !status) {
518                 unsigned char c = 0;
519                 char cc = 0;
520                 is.get(cc);
521                 c = cc;
522                 if ((c >= ' ' || c == '\t') && is) {
523                         buff.clear();
524
525                         if (c == '\\') { // first char == '\\'
526                                 do {
527                                         buff.push_back(c);
528                                         is.get(cc);
529                                         c = cc;
530                                 } while (c > ' ' && c != '\\' && is);
531                         } else {
532                                 do {
533                                         buff.push_back(c);
534                                         is.get(cc);
535                                         c = cc;
536                                 } while ((c >= ' ' || c == '\t') && c != '\\' && is);
537                         }
538
539                         if (c == '\\')
540                                 is.putback(c); // put it back
541                         status = LEX_TOKEN;
542                 }
543
544                 if (c == '\n')
545                         ++lineno;
546
547         }
548         if (status)
549                 return true;
550
551         status = is.eof() ? LEX_FEOF: LEX_UNDEF;
552         buff.clear();
553         return false;
554 }
555
556
557 bool Lexer::Pimpl::inputAvailable()
558 {
559         return is.good();
560 }
561
562
563 void Lexer::Pimpl::pushToken(string const & pt)
564 {
565         pushTok = pt;
566 }
567
568
569
570
571 //////////////////////////////////////////////////////////////////////
572 //
573 // Lexer
574 //
575 //////////////////////////////////////////////////////////////////////
576
577 Lexer::Lexer()
578         : pimpl_(new Pimpl(0, 0))
579 {}
580
581
582 void Lexer::init(LexerKeyword * tab, int num)
583 {
584          pimpl_ = new Pimpl(tab, num);
585 }
586
587
588 Lexer::~Lexer()
589 {
590         delete pimpl_;
591 }
592
593
594 bool Lexer::isOK() const
595 {
596         return pimpl_->inputAvailable();
597 }
598
599
600 void Lexer::setLineNumber(int l)
601 {
602         pimpl_->lineno = l;
603 }
604
605
606 int Lexer::lineNumber() const
607 {
608         return pimpl_->lineno;
609 }
610
611
612 istream & Lexer::getStream()
613 {
614         return pimpl_->is;
615 }
616
617
618 void Lexer::pushTable(LexerKeyword * tab, int num)
619 {
620         pimpl_->pushTable(tab, num);
621 }
622
623
624 void Lexer::popTable()
625 {
626         pimpl_->popTable();
627 }
628
629
630 void Lexer::printTable(ostream & os)
631 {
632         pimpl_->printTable(os);
633 }
634
635
636 void Lexer::printError(string const & message) const
637 {
638         pimpl_->printError(message);
639 }
640
641
642 bool Lexer::setFile(FileName const & filename)
643 {
644         return pimpl_->setFile(filename);
645 }
646
647
648 void Lexer::setStream(istream & i)
649 {
650         pimpl_->setStream(i);
651 }
652
653
654 void Lexer::setCommentChar(char c)
655 {
656         pimpl_->setCommentChar(c);
657 }
658
659
660 int Lexer::lex()
661 {
662         return pimpl_->lex();
663 }
664
665
666 int Lexer::getInteger() const
667 {
668         lastReadOk_ = pimpl_->status == LEX_DATA || pimpl_->status == LEX_TOKEN;
669         if (!lastReadOk_) {
670                 pimpl_->printError("integer token missing");
671                 return -1;
672         }
673
674         if (isStrInt(pimpl_->getString()))
675                 return convert<int>(pimpl_->getString());
676
677         lastReadOk_ = false;
678         pimpl_->printError("Bad integer `$$Token'");
679         return -1;
680 }
681
682
683 double Lexer::getFloat() const
684 {
685         // replace comma with dot in case the file was written with
686         // the wrong locale (should be rare, but is easy enough to
687         // avoid).
688         lastReadOk_ = pimpl_->status == LEX_DATA || pimpl_->status == LEX_TOKEN;
689         if (!lastReadOk_) {
690                 pimpl_->printError("float token missing");
691                 return -1;
692         }
693
694         string const str = subst(pimpl_->getString(), ",", ".");
695         if (isStrDbl(str))
696                 return convert<double>(str);
697
698         lastReadOk_ = false;
699         pimpl_->printError("Bad float `$$Token'");
700         return -1;
701 }
702
703
704 string const Lexer::getString() const
705 {
706         lastReadOk_ = pimpl_->status == LEX_DATA || pimpl_->status == LEX_TOKEN;
707
708         if (lastReadOk_)
709         return pimpl_->getString();
710
711         return string();
712 }
713
714
715 docstring const Lexer::getDocString() const
716 {
717         lastReadOk_ = pimpl_->status == LEX_DATA || pimpl_->status == LEX_TOKEN;
718
719         if (lastReadOk_)
720                 return pimpl_->getDocString();
721
722         return docstring();
723 }
724
725
726 // I would prefer to give a tag number instead of an explicit token
727 // here, but it is not possible because Buffer::readDocument uses
728 // explicit tokens (JMarc)
729 string const Lexer::getLongString(string const & endtoken)
730 {
731         string str;
732         string prefix;
733         bool firstline = true;
734
735         while (pimpl_->is) { //< eatLine only reads from is, not from pushTok
736                 if (!eatLine())
737                         // blank line in the file being read
738                         continue;
739
740                 string const token = trim(getString(), " \t");
741
742                 LYXERR(Debug::PARSER, "LongString: `" << getString() << '\'');
743
744                 // We do a case independent comparison, like searchKeyword does.
745                 if (compare_ascii_no_case(token, endtoken) == 0)
746                         break;
747
748                 string tmpstr = getString();
749                 if (firstline) {
750                         size_t i = tmpstr.find_first_not_of(' ');
751                         if (i != string::npos)
752                                 prefix = tmpstr.substr(0, i);
753                         firstline = false;
754                         LYXERR(Debug::PARSER, "Prefix = `" << prefix << "\'");
755                 }
756
757                 // further lines in long strings may have the same
758                 // whitespace prefix as the first line. Remove it.
759                 if (prefix.length() && prefixIs(tmpstr, prefix))
760                         tmpstr.erase(0, prefix.length() - 1);
761
762                 str += ltrim(tmpstr, "\t") + '\n';
763         }
764
765         if (!pimpl_->is)
766                 printError("Long string not ended by `" + endtoken + '\'');
767
768         return str;
769 }
770
771
772 bool Lexer::getBool() const
773 {
774         string const s = pimpl_->getString();   
775         if (s == "false" || s == "0") {
776                 lastReadOk_ = true;
777                 return false;
778         }
779         if (s == "true" || s == "1") {
780                 lastReadOk_ = true;
781                 return true;
782         }
783         pimpl_->printError("Bad boolean `$$Token'. "
784                                  "Use \"false\" or \"true\"");
785         lastReadOk_ = false;
786         return false;
787 }
788
789
790 bool Lexer::eatLine()
791 {
792         return pimpl_->eatLine();
793 }
794
795
796 bool Lexer::next(bool esc)
797 {
798         return pimpl_->next(esc);
799 }
800
801
802 bool Lexer::nextToken()
803 {
804         return pimpl_->nextToken();
805 }
806
807
808 void Lexer::pushToken(string const & pt)
809 {
810         pimpl_->pushToken(pt);
811 }
812
813
814 Lexer::operator void const *() const
815 {
816         // This behaviour is NOT the same as the streams which would
817         // use fail() here. However, our implementation of getString() et al.
818         // can cause the eof() and fail() bits to be set, even though we
819         // haven't tried to read 'em.
820         return lastReadOk_? this : 0;
821 }
822
823
824 bool Lexer::operator!() const
825 {
826         return !lastReadOk_;
827 }
828
829
830 Lexer & Lexer::operator>>(string & s)
831 {
832         if (isOK()) {
833                 next();
834                 s = getString();
835         } else {
836                 lastReadOk_ = false;
837         }
838         return *this;
839 }
840
841
842 Lexer & Lexer::operator>>(docstring & s)
843 {
844         if (isOK()) {
845                 next();
846                 s = getDocString();
847         } else {
848                 lastReadOk_ = false;
849         }
850         return *this;
851 }
852
853
854 Lexer & Lexer::operator>>(double & s)
855 {
856         if (isOK()) {
857                 next();
858                 s = getFloat();
859         } else {
860                 lastReadOk_ = false;
861         }
862         return *this;
863 }
864
865
866 Lexer & Lexer::operator>>(int & s)
867 {
868         if (isOK()) {
869                 next();
870                 s = getInteger();
871         } else {
872                 lastReadOk_ = false;
873         }
874         return *this;
875 }
876
877
878 Lexer & Lexer::operator>>(unsigned int & s)
879 {
880         if (isOK()) {
881                 next();
882                 s = getInteger();
883         } else {
884                 lastReadOk_ = false;
885         }
886         return *this;
887 }
888
889
890 Lexer & Lexer::operator>>(bool & s)
891 {
892         if (isOK()) {
893                 next();
894                 s = getBool();
895         } else {
896                 lastReadOk_ = false;
897         }
898         return *this;
899 }
900
901
902 Lexer & Lexer::operator>>(char & c)
903 {
904         string s;
905         operator>>(s);
906         if (!s.empty())
907                 c = s[0];
908         return *this;
909 }
910
911
912 // quotes a string, e.g. for use in preferences files or as an argument
913 // of the "log" dialog
914 string Lexer::quoteString(string const & arg)
915 {
916         string res;
917         res += '"';
918         res += subst(subst(arg, "\\", "\\\\"), "\"", "\\\"");
919         res += '"';
920         return res;
921 }
922
923
924 // same for docstring
925 docstring Lexer::quoteString(docstring const & arg)
926 {
927         docstring res;
928         res += '"';
929         res += subst(subst(arg, from_ascii("\\"), from_ascii("\\\\")), 
930                      from_ascii("\""), from_ascii("\\\""));
931         res += '"';
932         return res;
933 }
934
935
936 Lexer & Lexer::operator>>(char const * required)
937 {
938         string token;
939         *this >> token;
940         if (token != required) {
941                 LYXERR0("Missing '" << required << "'-tag in " << pimpl_->context 
942                         << ". Got " << token << " instead. Line: " << lineNumber());
943                 pushToken(token);
944         }
945         return *this;
946 }
947
948
949 bool Lexer::checkFor(char const * required)
950 {
951         string token;
952         *this >> token;
953         if (token == required)
954                 return true;
955         pushToken(token);
956         return false;
957 }
958
959
960 void Lexer::setContext(std::string const & str)
961 {
962         pimpl_->context = str;
963 }
964
965
966 } // namespace lyx