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