]> git.lyx.org Git - lyx.git/blob - src/Lexer.cpp
Fix #10778 (issue with CJK and language nesting)
[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         // Check the format of the file.
242         if (formats.isZippedFile(filename)) {
243                 LYXERR(Debug::LYXLEX, "lyxlex: compressed");
244                 // The check only outputs a debug message, because it triggers
245                 // a bug in compaq cxx 6.2, where is_open() returns 'true' for
246                 // a fresh new filebuf.  (JMarc)
247                 if (gz_.is_open() || istream::off_type(is.tellg()) > -1)
248                         LYXERR(Debug::LYXLEX, "Error in LyXLex::setFile: "
249                                 "file or stream already set.");
250                 gz_.open(filename.toFilesystemEncoding().c_str(), ios::in);
251                 is.rdbuf(&gz_);
252                 name = filename.absFileName();
253                 lineno = 0;
254                 if (!gz_.is_open() || !is.good())
255                         return false;
256         } else {
257                 LYXERR(Debug::LYXLEX, "lyxlex: UNcompressed");
258
259                 // The check only outputs a debug message, because it triggers
260                 // a bug in compaq cxx 6.2, where is_open() returns 'true' for
261                 // a fresh new filebuf.  (JMarc)
262                 if (fb_.is_open() || istream::off_type(is.tellg()) > 0) {
263                         LYXERR(Debug::LYXLEX, "Error in Lexer::setFile: "
264                                 "file or stream already set.");
265                 }
266                 fb_.open(filename.toSafeFilesystemEncoding().c_str(), ios::in);
267                 is.rdbuf(&fb_);
268                 name = filename.absFileName();
269                 lineno = 0;
270                 if (!fb_.is_open() || !is.good())
271                         return false;
272         }
273
274         // Skip byte order mark.
275         if (is.peek() == 0xef) {
276                 is.get();
277                 if (is.peek() == 0xbb) {
278                         is.get();
279                         LASSERT(is.get() == 0xbf, /**/);
280                 } else
281                         is.unget();
282         }
283
284         return true;
285 }
286
287
288 void Lexer::Pimpl::setStream(istream & i)
289 {
290         if (fb_.is_open() || istream::off_type(is.tellg()) > 0) {
291                 LYXERR(Debug::LYXLEX, "Error in Lexer::setStream: "
292                         "file or stream already set.");
293         }
294         is.rdbuf(i.rdbuf());
295         lineno = 0;
296 }
297
298
299 void Lexer::Pimpl::setCommentChar(char c)
300 {
301         commentChar = c;
302 }
303
304
305 bool Lexer::Pimpl::next(bool esc /* = false */)
306 {
307         if (!pushTok.empty()) {
308                 // There can have been a whole line pushed so
309                 // we extract the first word and leaves the rest
310                 // in pushTok. (Lgb)
311                 if (pushTok[0] == '\\' && pushTok.find(' ') != string::npos) {
312                         buff.clear();
313                         pushTok = split(pushTok, buff, ' ');
314                 } else {
315                         buff = pushTok;
316                         pushTok.clear();
317                 }
318                 status = LEX_TOKEN;
319                 return true;
320         }
321
322
323         char cc = 0;
324         status = 0;
325         while (is && !status) {
326                 is.get(cc);
327                 unsigned char c = cc;
328
329                 if (c == commentChar) {
330                         // Read rest of line (fast :-)
331 #if 1
332                         // That is not fast... (Lgb)
333                         string dummy;
334                         getline(is, dummy);
335
336                         LYXERR(Debug::LYXLEX, "Comment read: `" << c << dummy << '\'');
337 #else
338                         // unfortunately ignore is buggy (Lgb)
339                         is.ignore(100, '\n');
340 #endif
341                         ++lineno;
342                         continue;
343                 }
344
345                 if (c == '\"') {
346                         buff.clear();
347
348                         if (esc) {
349
350                                 do {
351                                         bool escaped = false;
352                                         is.get(cc);
353                                         c = cc;
354                                         if (c == '\r') continue;
355                                         if (c == '\\') {
356                                                 // escape the next char
357                                                 is.get(cc);
358                                                 c = cc;
359                                                 if (c == '\"' || c == '\\')
360                                                         escaped = true;
361                                                 else
362                                                         buff.push_back('\\');
363                                         }
364                                         buff.push_back(c);
365
366                                         if (!escaped && c == '\"')
367                                                 break;
368                                 } while (c != '\n' && is);
369
370                         } else {
371
372                                 do {
373                                         is.get(cc);
374                                         c = cc;
375                                         if (c != '\r')
376                                                 buff.push_back(c);
377                                 } while (c != '\"' && c != '\n' && is);
378
379                         }
380
381                         if (c != '\"') {
382                                 printError("Missing quote");
383                                 if (c == '\n')
384                                         ++lineno;
385                         }
386
387                         buff.resize(buff.size() - 1);
388                         status = LEX_DATA;
389                         break;
390                 }
391
392                 if (c == ',')
393                         continue;              /* Skip ','s */
394
395                 // using relational operators with chars other
396                 // than == and != is not safe. And if it is done
397                 // the type _have_ to be unsigned. It usually a
398                 // lot better to use the functions from cctype
399                 if (c > ' ' && is)  {
400                         buff.clear();
401
402                         do {
403                                 if (esc && c == '\\') {
404                                         // escape the next char
405                                         is.get(cc);
406                                         c = cc;
407                                         //escaped = true;
408                                 }
409                                 buff.push_back(c);
410                                 is.get(cc);
411                                 c = cc;
412                         } while (c > ' ' && c != ',' && is);
413                         status = LEX_TOKEN;
414                 }
415
416                 if (c == '\r' && is) {
417                         // The Windows support has lead to the
418                         // possibility of "\r\n" at the end of
419                         // a line.  This will stop LyX choking
420                         // when it expected to find a '\n'
421                         is.get(cc);
422                         c = cc;
423                 }
424
425                 if (c == '\n')
426                         ++lineno;
427
428         }
429         if (status)
430                 return true;
431
432         status = is.eof() ? LEX_FEOF: LEX_UNDEF;
433         buff.clear();
434         return false;
435 }
436
437
438 int Lexer::Pimpl::searchKeyword(char const * const tag) const
439 {
440         LexerKeyword search_tag = { tag, 0 };
441         LexerKeyword * res =
442                 lower_bound(table, table + no_items,
443                             search_tag, CompareTags());
444         // use the compare_ascii_no_case instead of compare_no_case,
445         // because in turkish, 'i' is not the lowercase version of 'I',
446         // and thus turkish locale breaks parsing of tags.
447         if (res != table + no_items
448             && !compare_ascii_no_case(res->tag, tag))
449                 return res->code;
450         return LEX_UNDEF;
451 }
452
453
454 int Lexer::Pimpl::lex()
455 {
456         //NOTE: possible bug.
457         if (next() && status == LEX_TOKEN)
458                 return searchKeyword(getString().c_str());
459         return status;
460 }
461
462
463 bool Lexer::Pimpl::eatLine()
464 {
465         buff.clear();
466
467         unsigned char c = '\0';
468         char cc = 0;
469         while (is && c != '\n') {
470                 is.get(cc);
471                 c = cc;
472                 //LYXERR(Debug::LYXLEX, "Lexer::EatLine read char: `" << c << '\'');
473                 if (c != '\r' && is)
474                         buff.push_back(c);
475         }
476
477         if (c == '\n') {
478                 ++lineno;
479                 buff.resize(buff.size() - 1);
480                 status = LEX_DATA;
481                 return true;
482         } else if (buff.length() > 0) { // last line
483                 status = LEX_DATA;
484                 return true;
485         } else {
486                 return false;
487         }
488 }
489
490
491 bool Lexer::Pimpl::nextToken()
492 {
493         if (!pushTok.empty()) {
494                 // There can have been a whole line pushed so
495                 // we extract the first word and leaves the rest
496                 // in pushTok. (Lgb)
497                 if (pushTok[0] == '\\' && pushTok.find(' ') != string::npos) {
498                         buff.clear();
499                         pushTok = split(pushTok, buff, ' ');
500                 } else {
501                         buff = pushTok;
502                         pushTok.clear();
503                 }
504                 status = LEX_TOKEN;
505                 return true;
506         }
507
508         status = 0;
509         while (is && !status) {
510                 unsigned char c = 0;
511                 char cc = 0;
512                 is.get(cc);
513                 c = cc;
514                 if ((c >= ' ' || c == '\t') && is) {
515                         buff.clear();
516
517                         if (c == '\\') { // first char == '\\'
518                                 do {
519                                         buff.push_back(c);
520                                         is.get(cc);
521                                         c = cc;
522                                 } while (c > ' ' && c != '\\' && is);
523                         } else {
524                                 do {
525                                         buff.push_back(c);
526                                         is.get(cc);
527                                         c = cc;
528                                 } while ((c >= ' ' || c == '\t') && c != '\\' && is);
529                         }
530
531                         if (c == '\\')
532                                 is.putback(c); // put it back
533                         status = LEX_TOKEN;
534                 }
535
536                 if (c == '\n')
537                         ++lineno;
538
539         }
540         if (status)
541                 return true;
542
543         status = is.eof() ? LEX_FEOF: LEX_UNDEF;
544         buff.clear();
545         return false;
546 }
547
548
549 bool Lexer::Pimpl::inputAvailable()
550 {
551         return is.good();
552 }
553
554
555 void Lexer::Pimpl::pushToken(string const & pt)
556 {
557         pushTok = pt;
558 }
559
560
561
562
563 //////////////////////////////////////////////////////////////////////
564 //
565 // Lexer
566 //
567 //////////////////////////////////////////////////////////////////////
568
569 Lexer::Lexer()
570         : pimpl_(new Pimpl(0, 0)), lastReadOk_(false)
571 {}
572
573
574 void Lexer::init(LexerKeyword * tab, int num)
575 {
576          pimpl_ = new Pimpl(tab, num);
577 }
578
579
580 Lexer::~Lexer()
581 {
582         delete pimpl_;
583 }
584
585
586 bool Lexer::isOK() const
587 {
588         return pimpl_->inputAvailable();
589 }
590
591
592 void Lexer::setLineNumber(int l)
593 {
594         pimpl_->lineno = l;
595 }
596
597
598 int Lexer::lineNumber() const
599 {
600         return pimpl_->lineno;
601 }
602
603
604 istream & Lexer::getStream()
605 {
606         return pimpl_->is;
607 }
608
609
610 void Lexer::pushTable(LexerKeyword * tab, int num)
611 {
612         pimpl_->pushTable(tab, num);
613 }
614
615
616 void Lexer::popTable()
617 {
618         pimpl_->popTable();
619 }
620
621
622 void Lexer::printTable(ostream & os)
623 {
624         pimpl_->printTable(os);
625 }
626
627
628 void Lexer::printError(string const & message) const
629 {
630         pimpl_->printError(message);
631 }
632
633
634 bool Lexer::setFile(FileName const & filename)
635 {
636         return pimpl_->setFile(filename);
637 }
638
639
640 void Lexer::setStream(istream & i)
641 {
642         pimpl_->setStream(i);
643 }
644
645
646 void Lexer::setCommentChar(char c)
647 {
648         pimpl_->setCommentChar(c);
649 }
650
651
652 int Lexer::lex()
653 {
654         return pimpl_->lex();
655 }
656
657
658 int Lexer::getInteger() const
659 {
660         lastReadOk_ = pimpl_->status == LEX_DATA || pimpl_->status == LEX_TOKEN;
661         if (!lastReadOk_) {
662                 pimpl_->printError("integer token missing");
663                 return -1;
664         }
665
666         if (isStrInt(pimpl_->getString()))
667                 return convert<int>(pimpl_->getString());
668
669         lastReadOk_ = false;
670         pimpl_->printError("Bad integer `$$Token'");
671         return -1;
672 }
673
674
675 double Lexer::getFloat() const
676 {
677         // replace comma with dot in case the file was written with
678         // the wrong locale (should be rare, but is easy enough to
679         // avoid).
680         lastReadOk_ = pimpl_->status == LEX_DATA || pimpl_->status == LEX_TOKEN;
681         if (!lastReadOk_) {
682                 pimpl_->printError("float token missing");
683                 return -1;
684         }
685
686         string const str = subst(pimpl_->getString(), ",", ".");
687         if (isStrDbl(str))
688                 return convert<double>(str);
689
690         lastReadOk_ = false;
691         pimpl_->printError("Bad float `$$Token'");
692         return -1;
693 }
694
695
696 string const Lexer::getString(bool trim) const
697 {
698         lastReadOk_ = pimpl_->status == LEX_DATA || pimpl_->status == LEX_TOKEN;
699
700         if (lastReadOk_)
701                 return trim ? support::trim(pimpl_->getString(), "\t ") : pimpl_->getString();
702
703         return string();
704 }
705
706
707 docstring const Lexer::getDocString(bool trim) const
708 {
709         lastReadOk_ = pimpl_->status == LEX_DATA || pimpl_->status == LEX_TOKEN;
710
711         if (lastReadOk_)
712                 return trim ? support::trim(pimpl_->getDocString(), "\t ") : pimpl_->getDocString();
713
714         return docstring();
715 }
716
717
718 // I would prefer to give a tag number instead of an explicit token
719 // here, but it is not possible because Buffer::readDocument uses
720 // explicit tokens (JMarc)
721 string const Lexer::getLongString(string const & endtoken)
722 {
723         string str;
724         string prefix;
725         bool firstline = true;
726
727         while (pimpl_->is) { //< eatLine only reads from is, not from pushTok
728                 if (!eatLine())
729                         // blank line in the file being read
730                         continue;
731
732                 string const token = trim(getString(), " \t");
733
734                 LYXERR(Debug::PARSER, "LongString: `" << getString() << '\'');
735
736                 // We do a case independent comparison, like searchKeyword does.
737                 if (compare_ascii_no_case(token, endtoken) == 0)
738                         break;
739
740                 string tmpstr = getString();
741                 if (firstline) {
742                         size_t i = tmpstr.find_first_not_of(' ');
743                         if (i != string::npos)
744                                 prefix = tmpstr.substr(0, i);
745                         firstline = false;
746                         LYXERR(Debug::PARSER, "Prefix = `" << prefix << "\'");
747                 }
748
749                 // further lines in long strings may have the same
750                 // whitespace prefix as the first line. Remove it.
751                 if (prefix.length() && prefixIs(tmpstr, prefix))
752                         tmpstr.erase(0, prefix.length() - 1);
753
754                 str += ltrim(tmpstr, "\t") + '\n';
755         }
756
757         if (!pimpl_->is)
758                 printError("Long string not ended by `" + endtoken + '\'');
759
760         return str;
761 }
762
763
764 bool Lexer::getBool() const
765 {
766         string const s = pimpl_->getString();   
767         if (s == "false" || s == "0") {
768                 lastReadOk_ = true;
769                 return false;
770         }
771         if (s == "true" || s == "1") {
772                 lastReadOk_ = true;
773                 return true;
774         }
775         pimpl_->printError("Bad boolean `$$Token'. "
776                                  "Use \"false\" or \"true\"");
777         lastReadOk_ = false;
778         return false;
779 }
780
781
782 bool Lexer::eatLine()
783 {
784         return pimpl_->eatLine();
785 }
786
787
788 bool Lexer::next(bool esc)
789 {
790         return pimpl_->next(esc);
791 }
792
793
794 bool Lexer::nextToken()
795 {
796         return pimpl_->nextToken();
797 }
798
799
800 void Lexer::pushToken(string const & pt)
801 {
802         pimpl_->pushToken(pt);
803 }
804
805
806 Lexer::operator void const *() const
807 {
808         // This behaviour is NOT the same as the streams which would
809         // use fail() here. However, our implementation of getString() et al.
810         // can cause the eof() and fail() bits to be set, even though we
811         // haven't tried to read 'em.
812         return lastReadOk_? this : 0;
813 }
814
815
816 bool Lexer::operator!() const
817 {
818         return !lastReadOk_;
819 }
820
821
822 Lexer & Lexer::operator>>(string & s)
823 {
824         if (isOK()) {
825                 next();
826                 s = getString();
827         } else {
828                 lastReadOk_ = false;
829         }
830         return *this;
831 }
832
833
834 Lexer & Lexer::operator>>(docstring & s)
835 {
836         if (isOK()) {
837                 next();
838                 s = getDocString();
839         } else {
840                 lastReadOk_ = false;
841         }
842         return *this;
843 }
844
845
846 Lexer & Lexer::operator>>(double & s)
847 {
848         if (isOK()) {
849                 next();
850                 s = getFloat();
851         } else {
852                 lastReadOk_ = false;
853         }
854         return *this;
855 }
856
857
858 Lexer & Lexer::operator>>(int & s)
859 {
860         if (isOK()) {
861                 next();
862                 s = getInteger();
863         } else {
864                 lastReadOk_ = false;
865         }
866         return *this;
867 }
868
869
870 Lexer & Lexer::operator>>(unsigned int & s)
871 {
872         if (isOK()) {
873                 next();
874                 s = getInteger();
875         } else {
876                 lastReadOk_ = false;
877         }
878         return *this;
879 }
880
881
882 Lexer & Lexer::operator>>(bool & s)
883 {
884         if (isOK()) {
885                 next();
886                 s = getBool();
887         } else {
888                 lastReadOk_ = false;
889         }
890         return *this;
891 }
892
893
894 Lexer & Lexer::operator>>(char & c)
895 {
896         string s;
897         operator>>(s);
898         if (!s.empty())
899                 c = s[0];
900         return *this;
901 }
902
903
904 // quotes a string, e.g. for use in preferences files or as an argument
905 // of the "log" dialog
906 string Lexer::quoteString(string const & arg)
907 {
908         string res;
909         res += '"';
910         res += subst(subst(arg, "\\", "\\\\"), "\"", "\\\"");
911         res += '"';
912         return res;
913 }
914
915
916 // same for docstring
917 docstring Lexer::quoteString(docstring const & arg)
918 {
919         docstring res;
920         res += '"';
921         res += subst(subst(arg, from_ascii("\\"), from_ascii("\\\\")), 
922                      from_ascii("\""), from_ascii("\\\""));
923         res += '"';
924         return res;
925 }
926
927
928 Lexer & Lexer::operator>>(char const * required)
929 {
930         string token;
931         *this >> token;
932         if (token != required) {
933                 LYXERR0("Missing '" << required << "'-tag in " << pimpl_->context 
934                         << ". Got " << token << " instead. Line: " << lineNumber());
935                 pushToken(token);
936         }
937         return *this;
938 }
939
940
941 bool Lexer::checkFor(char const * required)
942 {
943         string token;
944         *this >> token;
945         if (token == required)
946                 return true;
947         pushToken(token);
948         return false;
949 }
950
951
952 void Lexer::setContext(std::string const & str)
953 {
954         pimpl_->context = str;
955 }
956
957
958 } // namespace lyx