root/trunk/scilexer/LexD.cxx

Revision 608, 37.1 kB (checked in by SnakE, 3 years ago)

Hex floats MUST have exponent

Line 
1 // Copyright 2008 Sergey "SnakE" Gromov
2 // snake.scaly@gmail.com
3
4 // Distributed under the Boost Software License, Version 1.0. (See
5 // accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt)
7
8 #include <assert.h>
9 #include <ctype.h>
10 #include <stddef.h>
11 #include <string.h>
12 #include <stdlib.h>
13
14 #include <memory>
15 #include <string>
16 #include <functional>
17
18 #include "Accessor.h"
19 #include "PropSet.h"
20 #include "KeyWords.h"
21 #include "SciLexer.h"
22 #include "Scintilla.h"
23
24 namespace dlex
25 {
26
27 static const int eol = -2;      // end of line
28 static const int range = -3;    // range operator, ".."
29
30 class Folder
31 {
32 public:
33
34     Folder() : nest(0), opened(0), compact(false), level(0)
35     {
36     }
37
38     void init(int nest, bool compact)
39     {
40         this->nest = nest;
41         this->compact = compact;
42         opened = 0;
43         level = nest;
44     }
45
46     void newLine()
47     {
48         opened = 0;
49         level = nest;
50     }
51
52     void open()
53     {
54         if (level > nest)
55             level = nest;
56         ++nest;
57         ++opened;
58     }
59
60     void close()
61     {
62         if (compact && level > nest)
63             level = nest;
64         if (nest > 0)
65             --nest;
66         if (!compact && level > nest)
67             level = nest;
68         if (opened > 0)
69             --opened;
70     }
71
72     int getNesting() const
73     {
74         return nest;
75     }
76
77     bool isFoldingPoint() const
78     {
79         return opened > 0;
80     }
81
82     int getLevel() const
83     {
84         return level;
85     }
86
87 private:
88
89     int nest;  ///< current nesting level
90     int opened; ///< number of blocks opened and not closed on this line
91     bool compact;   ///< compact mode folds up to the next folding point of the same nest
92     int level;
93 };
94
95 static Folder folder;
96 static bool foldComments = false;   // enable folding comments
97
98 /**
99     \brief Tests whether str matches one of the words in the given range.
100     \return Returns that word if matched.
101 */
102 template <class InStr, class WordIter>
103 static WordIter IsKeyword(InStr const & str, WordIter firstWord, WordIter lastWord)
104 {
105     for (; firstWord != lastWord; ++firstWord)
106         if (str.compare(*firstWord) == 0)
107             break;
108     return firstWord;
109 }
110
111 #include <stdarg.h>
112
113 static void log(char const * format...)
114 {
115     FILE * f = fopen("c:\\home\\snake\\src\\scintilla\\work\\lexd.log", "a");
116     if (f)
117     {
118         va_list ap;
119         va_start(ap, format);
120         vfprintf(f, format, ap);
121         va_end(ap);
122         fputc('\n', f);
123         fclose(f);
124     }
125 }
126
127 class Colourizer
128 {
129 public:
130
131     Colourizer() {}
132
133     Colourizer(Accessor & styler, WordList * * words, unsigned cur, unsigned next)
134         : m_styler(&styler)
135         , m_words(words)
136         , m_cur(cur)
137         , m_next(next)
138     {
139     }
140
141     // colourize all preceding characters, but not the current
142     void colourExcl(int style)
143     {
144         if (m_cur > 0)
145             m_styler->ColourTo(m_cur-1, style);
146     }
147
148     // colourize all characters including current
149     void colourIncl(int style)
150     {
151         if (m_next > 0)
152             m_styler->ColourTo(m_next-1, style);
153     }
154
155     // checks whether the given string matches a keyword
156     bool isKeyword(std::string const & s, int group)
157     {
158         char * * end = m_words[group]->words + m_words[group]->len;
159         return IsKeyword(s, m_words[group]->words, end) != end;
160     }
161
162 private:
163     Accessor * m_styler;
164     WordList * * m_words;
165     unsigned m_cur, m_next;
166 };
167
168 struct State;
169 typedef std::auto_ptr<State> StatePtr;
170 struct State
171 {
172     // returns a style associated with this state
173     virtual int getStyle() const = 0;
174     // sub-style helps distinguish between various flavours of a single style
175     virtual int getSubStyle() const {return 0;}
176     // can either return this or a new instance of another state
177     virtual StatePtr onChar(int ch, Colourizer colourizer) = 0;
178     // to be able to delete inctances
179     virtual ~State() {}
180 };
181
182 StatePtr NewDefaultState();
183
184 struct NullState : State
185 {
186     virtual int getStyle() const {return 0;}
187     virtual int getSubStyle() const {return 1;}
188     StatePtr onChar(int ch, Colourizer colourizer)
189     {
190         return StatePtr(new NullState);
191     }
192 };
193
194 //
195 // Numbers
196 //
197
198 struct NumberPart
199 {
200     enum type
201     {
202         integer,    // integer number, or an integer part of a floating point
203         fractional, // fractional part of a floating point
204         exponent    // exponent part of a floating point
205     };
206 };
207
208 // checks whether a char is a decimal digit
209 // if *first* is true, only 1-9 are valid digits
210 static bool IsDecimal(int ch, bool first)
211 {
212     return ch >= '1' && ch <= '9' || !first && ch == '0';
213 }
214
215 static StatePtr NewDecimalNumber(NumberPart::type t);
216
217 struct Exponent : State
218 {
219     virtual int getStyle() const {return SCE_D_NUMBER;}
220     StatePtr onChar(int ch, Colourizer colourizer)
221     {
222         if (ch == '+' || ch == '-')
223             return NewDecimalNumber(NumberPart::exponent);
224         else
225             return NewDecimalNumber(NumberPart::exponent)->onChar(ch, colourizer);
226     }
227 };
228
229 struct IntegerSuffix : State
230 {
231     IntegerSuffix() : first(-1) {}
232     virtual int getStyle() const {return SCE_D_NUMBER;}
233     StatePtr onChar(int ch, Colourizer colourizer)
234     {
235         if (first == -1 && strchr("LuUfFi", ch))
236         {
237             first = ch;
238             return StatePtr(new IntegerSuffix(*this));
239         }
240         if (
241             first == 'L' && (ch == 'u' || ch == 'U' || ch == 'i')
242             || (first == 'u' || first == 'U') && ch == 'L'
243             || (first == 'f' || first == 'F') && ch == 'i'
244             )
245         {
246             colourizer.colourIncl(SCE_D_NUMBER);
247             return NewDefaultState();
248         }
249         colourizer.colourExcl(SCE_D_NUMBER);
250         return NewDefaultState()->onChar(ch, colourizer);
251     }
252 private:
253     int first;
254 };
255
256 struct FloatSuffix : State
257 {
258     FloatSuffix() : first(-1) {}
259     virtual int getStyle() const {return SCE_D_NUMBER;}
260     StatePtr onChar(int ch, Colourizer colourizer)
261     {
262         if (first == -1 && strchr("fFLi", ch))
263         {
264             first = ch;
265             return StatePtr(new FloatSuffix(*this));
266         }
267         if ((first == 'f' || first == 'F' || first == 'L') && ch == 'i')
268         {
269             colourizer.colourIncl(SCE_D_NUMBER);
270             return NewDefaultState();
271         }
272         colourizer.colourExcl(SCE_D_NUMBER);
273         return NewDefaultState()->onChar(ch, colourizer);
274     }
275 private:
276     int first;
277 };
278
279 template <class DigitTestPred>
280 class Number : public State
281 {
282     typedef Number<DigitTestPred> this_type;
283 public:
284     Number(DigitTestPred test, char const * exponent, NumberPart::type part) : test(test), exponent(exponent), part(part) {}
285     virtual int getStyle() const {return SCE_D_NUMBER;}
286     StatePtr onChar(int ch, Colourizer colourizer)
287     {
288         if (part == NumberPart::integer && ch == '.')
289             return StatePtr(new this_type(test, exponent, NumberPart::fractional));
290
291         if (part == NumberPart::fractional && strchr(exponent, ch))
292             return StatePtr(new Exponent);
293
294         if (!(test(ch) || ch == '_'))
295         {
296             colourizer.colourExcl(SCE_D_NUMBER);
297             // HACK: Hex floats *must* have exponent
298             if (part == NumberPart::fractional && strchr(exponent, 'p'))
299             {
300                 colourizer.colourIncl(SCE_D_NUMBER_ERROR);
301                 return NewDefaultState();
302             }
303             else
304             {
305                 StatePtr suffix;
306                 if (part == NumberPart::integer)
307                     suffix = StatePtr(new IntegerSuffix);
308                 else
309                     suffix = StatePtr(new FloatSuffix);
310                 return suffix->onChar(ch, colourizer);
311             }
312         }
313
314         return StatePtr(new this_type(*this));
315     }
316 private:
317     DigitTestPred test;
318     char const * exponent;
319     NumberPart::type part;
320 };
321
322 template <class DigitTestPred>
323 inline StatePtr NewNumber(DigitTestPred p, char const * e, NumberPart::type t)
324 {
325     return StatePtr(new Number<DigitTestPred>(p, e, t));
326 }
327
328 static StatePtr NewDecimalNumber(NumberPart::type t)
329 {
330     return NewNumber(std::bind2nd(std::ptr_fun(IsDecimal), false), "eE", t);
331 }
332
333 static StatePtr NewHexNumber(NumberPart::type t)
334 {
335     return NewNumber(isxdigit, "pP", t);
336 }
337
338 struct RadixDigits : State
339 {
340     RadixDigits(unsigned radix) : radix(radix), error(false) {}
341     virtual int getStyle() const {return error ? SCE_D_NUMBER_ERROR : SCE_D_NUMBER;}
342     StatePtr onChar(int ch, Colourizer colourizer)
343     {
344         unsigned c = tolower(ch);
345         if (c == '_')
346             ;
347         else if (c >= '0' && c <= '9')
348         {
349             if (!error && c - '0' >= radix)
350             {
351                 error = true;
352                 colourizer.colourExcl(SCE_D_NUMBER);
353             }
354         }
355         else if (!(c >= 'a' && c <= 'z' && c - 'a' + 10 < radix))
356         {
357             colourizer.colourExcl(getStyle());
358             return StatePtr(new IntegerSuffix)->onChar(ch, colourizer);
359         }
360         return StatePtr(new RadixDigits(*this));
361     }
362 private:
363     unsigned radix;
364     bool error;
365 };
366
367 StatePtr NewMaybeKeyword(std::string beg);
368
369 struct MaybeFloat : State
370 {
371     MaybeFloat(Colourizer dot) : dot(dot) {}
372     virtual int getStyle() const {return SCE_D_OPERATOR;}
373     StatePtr onChar(int ch, Colourizer colourizer)
374     {
375         if (IsDecimal(ch, false))
376             return NewDecimalNumber(NumberPart::fractional);    // phew, it was a number after all.
377         if (ch == '_')
378         {
379             // it still may be an identifier after a dot
380             underscore += ch;
381             return StatePtr(new MaybeFloat(*this));
382         }
383         // dot was a dot, and the rest is either an ident or a keyword
384         dot.colourIncl(SCE_D_OPERATOR);
385         return NewMaybeKeyword(underscore)->onChar(ch, colourizer);
386     }
387 private:
388     Colourizer dot;
389     std::string underscore;
390 };
391
392 struct NumberOpen : State
393 {
394     virtual int getStyle() const {return SCE_D_NUMBER;}
395     StatePtr onChar(int ch, Colourizer colourizer)
396     {
397         if (ch == 'x' || ch == 'X')
398             return NewHexNumber(NumberPart::integer);
399         if (ch == 'b' || ch == 'B')
400             return StatePtr(new RadixDigits(2));
401         if (ch == '.')
402             return NewDecimalNumber(NumberPart::fractional);
403         return StatePtr(new RadixDigits(8))->onChar(ch, colourizer);
404     }
405 };
406
407 //
408 // Strings
409 //
410
411 // For strings, sub-style is a string type:
412 // LSB is the stringt type;
413 // next-to-LSB meaning is different for different strings
414
415 struct StringType
416 {
417     enum type
418     {
419         // regular string; next-to-LSB byte is not used
420         regular,
421
422         // wysiwyg string; next-to-LSB byte contains a string termination character
423         wysiwyg,
424
425         // hexadecimal string; next-to-LSB byte specifies whether a string contained an odd number of hex digits till yet
426         hex,
427
428         // delimited string; next-to-LSB byte contains number of skipped double-quotes for back-tracking
429         delimited
430     };
431 };
432
433 StatePtr NewDoubleQuotedString();
434
435 struct HexEscape : State
436 {
437     HexEscape(int expect, StatePtr next) : numDigits(expect), nextState(next) {}
438     virtual int getStyle() const {return SCE_D_STRING_ERROR;}
439     virtual int getSubStyle() const {return StringType::regular;}
440     virtual StatePtr onChar(int ch, Colourizer colourizer)
441     {
442         if (!isxdigit(ch))
443         {
444             colourizer.colourExcl(SCE_D_STRING_ERROR);
445             return nextState->onChar(ch, colourizer);
446         }
447         if (--numDigits == 0)
448         {
449             colourizer.colourIncl(SCE_D_ESCAPE);
450             return nextState;
451         }
452         return StatePtr(new HexEscape(*this));
453     }
454 private:
455     int numDigits;
456     StatePtr nextState;
457 };
458
459 struct OctEscape : State
460 {
461     OctEscape(StatePtr next) : numDigits(1), nextState(next) {}
462     virtual int getStyle() const {return SCE_D_ESCAPE;}
463     virtual int getSubStyle() const {return StringType::regular;}
464     virtual StatePtr onChar(int ch, Colourizer colourizer)
465     {
466         if (!(ch >= '0' && ch <= '7'))
467         {
468             colourizer.colourExcl(SCE_D_ESCAPE);
469             return nextState->onChar(ch, colourizer);
470         }
471         if (++numDigits == 3)
472         {
473             colourizer.colourIncl(SCE_D_ESCAPE);
474             return nextState;
475         }
476         return StatePtr(new OctEscape(*this));
477     }
478 private:
479     int numDigits;
480     StatePtr nextState;
481 };
482
483 struct NamedEntityEscape : State
484 {
485     NamedEntityEscape(StatePtr next) : nextState(next) {}
486     virtual int getStyle() const {return SCE_D_STRING_ERROR;}
487     virtual int getSubStyle() const {return StringType::regular;}
488     virtual StatePtr onChar(int ch, Colourizer colourizer)
489     {
490         if (ch == ';')
491         {
492             colourizer.colourIncl(SCE_D_ESCAPE);
493             return nextState;
494         }
495         if (!isalnum(ch))
496         {
497             colourizer.colourExcl(SCE_D_STRING_ERROR);
498             return nextState->onChar(ch, colourizer);
499         }
500         return StatePtr(new NamedEntityEscape(*this));
501     }
502 private:
503     StatePtr nextState;
504 };
505
506 struct EscapeString : State
507 {
508     EscapeString(StatePtr next) : nextState(next) {}
509     virtual int getStyle() const {return SCE_D_ESCAPE_ERROR;}
510     virtual StatePtr onChar(int ch, Colourizer colourizer)
511     {
512         if (ch == 'x')
513             return StatePtr(new HexEscape(2, nextState));
514         else if (ch == 'u')
515             return StatePtr(new HexEscape(4, nextState));
516         else if (ch == 'U')
517             return StatePtr(new HexEscape(8, nextState));
518         else if (ch >= '0' && ch <= '7')
519             return StatePtr(new OctEscape(nextState));
520         else if (ch == '&')
521             return StatePtr(new NamedEntityEscape(nextState));
522         else if (strchr("'\"?\\abfnrtv", ch))
523             colourizer.colourIncl(SCE_D_ESCAPE);
524         else
525             colourizer.colourIncl(SCE_D_ESCAPE_ERROR);
526         return nextState;
527     }
528 private:
529     StatePtr nextState;
530 };
531
532 struct StringPostfix : State
533 {
534     virtual int getStyle() const {return SCE_D_IDENT;}
535     virtual StatePtr onChar(int ch, Colourizer colourizer)
536     {
537         if (ch == 'c' || ch == 'w' || ch == 'd')
538         {
539             colourizer.colourIncl(SCE_D_STRING);
540             return NewDefaultState();
541         }
542         return NewDefaultState()->onChar(ch, colourizer);
543     }
544 };
545
546 struct DoubleQuotedString : State
547 {
548     virtual int getStyle() const {return SCE_D_STRING;}
549     virtual int getSubStyle() const {return StringType::regular;}
550     virtual StatePtr onChar(int ch, Colourizer colourizer)
551     {
552         if (ch == '"')
553         {
554             colourizer.colourIncl(SCE_D_STRING);
555             return StatePtr(new StringPostfix);
556         }
557         if (ch == '\\')
558         {
559             colourizer.colourExcl(SCE_D_STRING);
560             return StatePtr(new EscapeString(StatePtr(new DoubleQuotedString)));
561         }
562         return StatePtr(new DoubleQuotedString);
563     }
564 };
565
566 StatePtr NewDoubleQuotedString()
567 {
568     return StatePtr(new DoubleQuotedString);
569 }
570
571 struct WysiwygString : State
572 {
573     WysiwygString(int ch) : m_end(ch) {}
574     virtual int getStyle() const {return SCE_D_STRING;}
575     virtual int getSubStyle() const {return StringType::wysiwyg | (m_end << 8);}
576     virtual StatePtr onChar(int ch, Colourizer colourizer)
577     {
578         if (ch != m_end)
579             return StatePtr(new WysiwygString(*this));
580
581         colourizer.colourIncl(getStyle());
582         return StatePtr(new StringPostfix);
583     }
584 private:
585     int m_end;
586 };
587
588 struct HexString : State
589 {
590     HexString(bool odd = false) : oddDigit(odd) {}
591     virtual int getStyle() const {return SCE_D_STRING;}
592     virtual int getSubStyle() const {return StringType::hex | (oddDigit << 8);}
593     virtual StatePtr onChar(int ch, Colourizer colourizer)
594     {
595         if (ch == '"')
596         {
597             colourizer.colourExcl(SCE_D_STRING_ERROR);  // odd number of hex digits, if any
598             colourizer.colourIncl(SCE_D_STRING);
599             return StatePtr(new StringPostfix);
600         }
601         if (isxdigit(ch) || isspace(ch) || ch == eol)
602         {
603             if (isxdigit(ch))
604                 oddDigit = !oddDigit;
605             if (!oddDigit)
606                 colourizer.colourIncl(SCE_D_STRING);    // this byte is complete, with any space after it
607         }
608         else
609         {
610             // bad char in hex string
611             colourizer.colourExcl(SCE_D_STRING);
612             colourizer.colourIncl(SCE_D_STRING_ERROR);
613         }
614         return StatePtr(new HexString(*this));
615     }
616 private:
617     bool oddDigit;
618 };
619
620 struct DelimitedStringNesting : State
621 {
622     DelimitedStringNesting(char open, char close) : openChar(open), closeChar(close), level(1) {}
623     virtual int getStyle() const {return SCE_D_STRING;}
624     virtual StatePtr onChar(int ch, Colourizer colourizer)
625     {
626         if (ch == openChar)
627             ++level;
628         else if (ch == closeChar)
629             --level;
630         if (level == 0)
631         {
632             colourizer.colourIncl(SCE_D_STRING);
633             return StatePtr(new NullState);
634         }
635         return StatePtr(new DelimitedStringNesting(*this));
636     }
637 private:
638     char openChar, closeChar;
639     int level;
640 };
641
642 struct DelimitedStringSimple : State
643 {
644     DelimitedStringSimple(char end) : endChar(end) {}
645     virtual int getStyle() const {return SCE_D_STRING;}
646     virtual StatePtr onChar(int ch, Colourizer colourizer)
647     {
648         if (ch == endChar)
649         {
650             colourizer.colourIncl(SCE_D_STRING);
651             return StatePtr(new NullState);
652         }
653         return StatePtr(new DelimitedStringSimple(*this));
654     }
655 private:
656     char endChar;
657 };
658
659 struct DelimitedStringHeredoc : State
660 {
661     DelimitedStringHeredoc(std::string end) : endIdent(end) {}
662     virtual int getStyle() const {return SCE_D_STRING;}
663     virtual StatePtr onChar(int ch, Colourizer colourizer)
664     {
665         if (ch == eol)
666             currentIdent.clear();
667         else if (currentIdent.size() < endIdent.size())
668             currentIdent += ch;
669
670         if (currentIdent.size() == endIdent.size())
671         {
672             if (currentIdent.compare(endIdent) == 0)
673             {
674                 colourizer.colourIncl(SCE_D_STRING);
675                 return StatePtr(new NullState);
676             }
677             currentIdent += '$';    // never match until the next EOL
678         }
679
680         return StatePtr(new DelimitedStringHeredoc(*this));
681     }
682 private:
683     std::string endIdent;
684     std::string currentIdent;
685 };
686
687 struct DelimitedStringBegin : State
688 {
689     DelimitedStringBegin(std::string id) : ident(id) {}
690     virtual int getStyle() const {return SCE_D_STRING_ERROR;}
691     virtual StatePtr onChar(int ch, Colourizer colourizer)
692     {
693         if (ch != eol)
694             return StatePtr(new DelimitedStringBegin(*this));
695         colourizer.colourExcl(SCE_D_STRING_ERROR);
696         return StatePtr(new DelimitedStringHeredoc(ident));
697     }
698 private:
699     std::string ident;
700 };
701
702 struct DelimitedStringIdent : State
703 {
704     virtual int getStyle() const {return SCE_D_STRING;}
705     virtual StatePtr onChar(int ch, Colourizer colourizer)
706     {
707         if (isalnum(ch) || ch == '_')
708         {
709             ident += ch;
710             return StatePtr(new DelimitedStringIdent(*this));
711         }
712         colourizer.colourExcl(SCE_D_STRING);
713         return StatePtr(new DelimitedStringBegin(ident))->onChar(ch, colourizer);
714     }
715 private:
716     std::string ident;
717 };
718
719 struct DelimitedStringOpen : State
720 {
721     virtual int getStyle() const {return SCE_D_STRING;}
722     virtual StatePtr onChar(int ch, Colourizer colourizer)
723     {
724         static char const open[]   = "([{<";
725         static char const close[]  = ")]}>";
726         static char const single[] = "`-=~!@#$%^&*+\\|;:\'\",./?";
727
728         char const * c = strchr(open, ch);
729
730         if (c)
731             return StatePtr(new DelimitedStringNesting(ch, close[c-open]));
732         else if (strchr(single, ch))
733             return StatePtr(new DelimitedStringSimple(ch));
734         else
735             return StatePtr(new DelimitedStringIdent)->onChar(ch, colourizer);
736     }
737 };
738
739 struct DelimitedStringEnd : State
740 {
741     virtual int getStyle() const {return SCE_D_STRING_ERROR;}
742     virtual StatePtr onChar(int ch, Colourizer colourizer)
743     {
744         if (ch == '"')
745         {
746             colourizer.colourExcl(SCE_D_STRING_ERROR);
747             colourizer.colourIncl(SCE_D_STRING);
748             return StatePtr(new StringPostfix);
749         }
750         return StatePtr(new DelimitedStringEnd);
751     }
752 };
753
754 struct DelimitedString : State
755 {
756     DelimitedString() : state(new DelimitedStringOpen), skipQuotes(1) {}
757     virtual int getStyle() const {return state->getStyle();}
758     virtual int getSubStyle() const {return StringType::delimited | (skipQuotes << 8);}
759     virtual StatePtr onChar(int ch, Colourizer colourizer)
760     {
761         if (ch == '"')
762             ++skipQuotes;
763
764         state = state->onChar(ch, colourizer);
765
766         if (!state->getSubStyle())
767             return StatePtr(new DelimitedString(*this));
768         else
769             return StatePtr(new DelimitedStringEnd)->onChar(ch, colourizer);
770     }
771 private:
772     StatePtr state;
773     unsigned skipQuotes;
774 };
775
776 struct MaybeKeyword : State
777 {
778     MaybeKeyword(std::string beg = std::string()) : keyword(beg) {}
779     virtual int getStyle() const {return SCE_D_IDENT;}
780     virtual StatePtr onChar(int ch, Colourizer colourizer)
781     {
782         if (isalnum(ch) || ch == '_' || (!keyword.empty() && isdigit(ch)))
783         {
784             keyword += (char)ch;
785             return StatePtr(new MaybeKeyword(*this));
786         }
787
788         // end of a word, check whether it's a keyword
789
790         if (ch == '"')
791         {
792             if (keyword.compare("r") == 0)
793                 return StatePtr(new WysiwygString(ch));
794
795             if (keyword.compare("x") == 0)
796                 return StatePtr(new HexString);
797
798             if (keyword.compare("q") == 0)
799                 return StatePtr(new DelimitedString);
800         }
801
802         colourizer.colourExcl(colourizer.isKeyword(keyword, 0) ? SCE_D_KEYWORD : SCE_D_IDENT);
803         return NewDefaultState()->onChar(ch, colourizer);
804     }
805 private:
806     std::string keyword;
807 };
808
809 StatePtr NewMaybeKeyword(std::string beg)
810 {
811     return StatePtr(new MaybeKeyword(beg));
812 }
813 //
814 // Chars
815 //
816
817 struct CharacterEnd : State
818 {
819     virtual int getStyle() const {return SCE_D_CHAR_ERROR;}
820     virtual StatePtr onChar(int ch, Colourizer colourizer)
821     {
822         if (ch == '\'')
823         {
824             colourizer.colourExcl(SCE_D_CHAR_ERROR);
825             colourizer.colourIncl(SCE_D_CHAR);
826             return NewDefaultState();
827         }
828         else if (ch == eol)
829         {
830             colourizer.colourIncl(SCE_D_CHAR_ERROR);
831             return NewDefaultState();
832         }
833         return StatePtr(new CharacterEnd);
834     }
835 };
836
837 struct Character : State
838 {
839     virtual int getStyle() const {return SCE_D_CHAR_ERROR;}
840     virtual StatePtr onChar(int ch, Colourizer colourizer)
841     {
842         colourizer.colourExcl(SCE_D_CHAR);
843         if (ch == '\\')
844             return StatePtr(new EscapeString(StatePtr(new CharacterEnd)));
845         if (ch == eol)
846         {
847             colourizer.colourIncl(SCE_D_CHAR_ERROR);
848             return NewDefaultState();
849         }
850         if (ch == range || ch == '\'')
851             colourizer.colourIncl(SCE_D_CHAR_ERROR);
852         else
853             colourizer.colourIncl(SCE_D_CHAR);
854         return StatePtr(new CharacterEnd);
855     }
856 };
857
858 //
859 // Comments
860 //
861
862 // For stream comments, sub-style is a comment type:
863 // LSB is the comment type, '*' or '+';
864 // next-to-LSB is a number of '*' or '+' to skip to back-track to the comment start
865
866 static StatePtr NewDdocText();
867
868 struct DdocMacroBody : State
869 {
870     DdocMacroBody() : nest(1) {}
871     virtual int getStyle() const {return SCE_D_DDOC_MACRO;}
872     StatePtr onChar(int ch, Colourizer colourizer)
873     {
874         if (ch == '(')
875             ++nest;
876         else if (ch == ')' && --nest == 0)
877         {
878             colourizer.colourIncl(SCE_D_DDOC_MACRO);
879             return NewDdocText();
880         }
881         return StatePtr(new DdocMacroBody(*this));
882     }
883 private:
884     int nest;
885 };
886
887 struct DdocMacroOpen : State
888 {
889     virtual int getStyle() const {return SCE_D_DDOC;}
890     StatePtr onChar(int ch, Colourizer colourizer)
891     {
892         if (ch == '(')
893             return StatePtr(new DdocMacroBody);
894         return NewDdocText();
895     }
896 };
897
898 struct DdocText : State
899 {
900     virtual int getStyle() const {return SCE_D_DDOC;}
901     StatePtr onChar(int ch, Colourizer colourizer)
902     {
903         if (ch == '$')
904         {
905             colourizer.colourExcl(SCE_D_DDOC);
906             return StatePtr(new DdocMacroOpen);
907         }
908         return StatePtr(new DdocText);
909     }
910 };
911
912 static StatePtr NewDdocText()
913 {
914     return StatePtr(new DdocText);
915 }
916
917 struct DdocSection : State
918 {
919     virtual int getStyle() const {return SCE_D_DDOC;}
920     StatePtr onChar(int ch, Colourizer colourizer)
921     {
922         if (ch == ':')
923         {
924             colourizer.colourIncl(SCE_D_DDOC_SECTION);
925             return StatePtr(new DdocText);
926         }
927         if (!(isalnum(ch) || ch == '_'))
928             return StatePtr(new DdocText);
929         return StatePtr(new DdocSection);
930     }
931 };
932
933 struct DdocWaitSection : State
934 {
935     DdocWaitSection(int skip = 0) : skipChar(skip) {}
936     virtual int getStyle() const {return SCE_D_DDOC;}
937     StatePtr onChar(int ch, Colourizer colourizer)
938     {
939         if (isalpha(ch) || ch == '_')
940         {
941             colourizer.colourExcl(SCE_D_DDOC);
942             return StatePtr(new DdocSection);
943         }
944
945         if (ch == skipChar)
946             skipChar = 0;   // skip once
947         else if (isspace(ch))
948             ;
949         else
950             return StatePtr(new DdocText)->onChar(ch, colourizer);
951
952         return StatePtr(new DdocWaitSection(*this));
953     }
954 private:
955     int skipChar;   // for skipping leading stars and pluses in multiline comments
956 };
957
958 struct Comment : State
959 {
960     virtual int getStyle() const {return SCE_D_COMMENT;}
961     StatePtr onChar(int ch, Colourizer colourizer)
962     {
963         return StatePtr(new Comment);
964     }
965 };
966
967 struct EolMaybeDdoc : State
968 {
969     virtual int getStyle() const {return SCE_D_COMMENT;}
970     StatePtr onChar(int ch, Colourizer colourizer)
971     {
972         if (ch == '/')
973             return StatePtr(new DdocWaitSection);
974         else
975             return StatePtr(new Comment);
976     }
977 };
978
979 struct EolComment : State
980 {
981     EolComment() : state(new EolMaybeDdoc) {}
982     virtual int getStyle() const {return state->getStyle();}
983     StatePtr onChar(int ch, Colourizer colourizer)
984     {
985         if (ch == eol)
986         {
987             colourizer.colourExcl(getStyle());
988             if (getStyle() == SCE_D_DDOC_MACRO)     // macro not closed
989                 colourizer.colourIncl(SCE_D_DDOC_MACRO_ERROR);
990             return NewDefaultState();
991         }
992         state = state->onChar(ch, colourizer);
993         return StatePtr(new EolComment(*this));
994     }
995 private:
996     StatePtr state;
997 };
998
999 struct StreamMaybeDdoc : State
1000 {
1001     StreamMaybeDdoc(int type) : type(type) {}
1002     virtual int getStyle() const {return SCE_D_COMMENT;}
1003     StatePtr onChar(int ch, Colourizer colourizer)
1004     {
1005         if (ch == type)
1006         {
1007             colourizer.colourIncl(SCE_D_DDOC);
1008             return StatePtr(new DdocWaitSection(type));
1009         }
1010         else
1011             return StatePtr(new Comment);
1012     }
1013 private:
1014     int type;
1015 };
1016
1017 struct StreamComment : State
1018 {
1019     StreamComment(int type) : state(new StreamMaybeDdoc(type)), type(type), level(1), backtrackCount(1), levelUp(false), levelDown(false) {}
1020     virtual int getStyle() const {return state->getStyle();}
1021     virtual int getSubStyle() const {return type | (backtrackCount << 8);}
1022     StatePtr onChar(int ch, Colourizer colourizer)
1023     {
1024         if (ch == type)
1025             ++backtrackCount;
1026
1027         if (levelUp)
1028         {
1029             levelUp = false;
1030             if (ch == type)
1031                 ++level;
1032         }
1033         if (type == '+' && ch == '/')
1034             levelUp = true;     // can be a nesting comment opening sequence
1035
1036         if (levelDown)
1037         {
1038             levelDown = false;
1039             if (ch == '/' && --level == 0)
1040             {
1041                 // end of stream comment
1042                 levelDownColourizer.colourExcl(getStyle());
1043                 colourizer.colourIncl(
1044                     getStyle() == SCE_D_COMMENT ?
1045                         SCE_D_COMMENT :
1046                         getStyle() == SCE_D_DDOC_MACRO ?
1047                             SCE_D_DDOC_MACRO_ERROR :
1048                             SCE_D_DDOC
1049                     );
1050                 if (foldComments)
1051                     folder.close();
1052                 return NewDefaultState()->onChar(ch, colourizer);
1053             }
1054         }
1055         if (ch == type)
1056         {
1057             levelDownColourizer = colourizer;
1058             levelDown = true;   // can be a comment closing sequence
1059         }
1060
1061         state = state->onChar(ch, colourizer);
1062
1063         if (ch == eol && state->getStyle() == SCE_D_DDOC)
1064             state = StatePtr(new DdocWaitSection(type));
1065
1066         return StatePtr(new StreamComment(*this));
1067     }
1068
1069 private:
1070
1071     StatePtr state;
1072     int type;   // either '*' or '+'
1073     int level;  // level for nesting comments
1074     int backtrackCount; // number of '*' or '+' from the start of this comment
1075     bool levelUp;   // if found '/' and type == '+'
1076     bool levelDown; // if found 'type'
1077     Colourizer levelDownColourizer;     // to close comment correctly if there is a macro error
1078 };
1079
1080 struct MaybeComment : State
1081 {
1082     int getStyle() const {return SCE_D_OPERATOR;}
1083     StatePtr onChar(int ch, Colourizer colourizer)
1084     {
1085         if (ch == '/')
1086             return StatePtr(new EolComment);
1087         if (ch == '*' || ch == '+')
1088         {
1089             if (foldComments)
1090                 folder.open();
1091             return StatePtr(new StreamComment(ch));
1092         }
1093
1094         // otherwise it's just a division
1095         colourizer.colourExcl(SCE_D_OPERATOR);
1096
1097         // process the ch in default state
1098         return NewDefaultState()->onChar(ch, colourizer);
1099     }
1100 };
1101
1102 //
1103 // Main state
1104 //
1105
1106 struct DefaultState : State
1107 {
1108     virtual int getStyle() const {return SCE_D_DEFAULT;}
1109     virtual StatePtr onChar(int ch, Colourizer colourizer)
1110     {
1111         colourizer.colourExcl(SCE_D_DEFAULT);
1112
1113         if (isalpha(ch) || ch == '_')
1114             return StatePtr(new MaybeKeyword)->onChar(ch, colourizer);
1115
1116         if (IsDecimal(ch, true))
1117             return NewDecimalNumber(NumberPart::integer);
1118         if (ch == '0')
1119             return StatePtr(new NumberOpen);
1120         if (ch == '.')
1121             return StatePtr(new MaybeFloat(colourizer));
1122
1123         if (ch == '\'')
1124             return StatePtr(new Character);
1125         if (ch == '"')
1126             return StatePtr(new DoubleQuotedString);
1127         if (ch == '`')
1128             return StatePtr(new WysiwygString(ch));
1129         if (ch == '\\')
1130             return StatePtr(new EscapeString(StatePtr(new DefaultState)));
1131         if (ch == '/')
1132             return StatePtr(new MaybeComment);
1133
1134         if (strchr("~!$%^&*=-+\\|,.?;:()[]{}<>", ch) || ch == range)
1135             colourizer.colourIncl(SCE_D_OPERATOR);
1136
1137         if (ch == '{')
1138             folder.open();
1139         if (ch == '}')
1140             folder.close();
1141
1142         return StatePtr(new DefaultState);
1143     }
1144 };
1145
1146 StatePtr NewDefaultState()
1147 {
1148     return StatePtr(new DefaultState);
1149 }
1150
1151 // returns the previous char and adjusts position to point to that char
1152 static int PreviousChar(unsigned & pos, Accessor & styler)
1153 {
1154     if (pos == 0)
1155         return 0;
1156
1157     --pos;
1158     int ch = (unsigned char) styler.SafeGetCharAt(pos);
1159
1160     if (pos > 0)
1161     {
1162         int prevCh = (unsigned char) styler.SafeGetCharAt(pos-1);
1163         if (styler.IsLeadByte((char)prevCh))
1164         {
1165             --pos;
1166             ch |= prevCh << 8;
1167         }
1168         else if (ch == '\n' && prevCh == '\r')
1169             --pos;
1170     }
1171
1172     if (ch == '\r' || ch == '\n')
1173         ch = eol;
1174
1175     return ch;
1176 }
1177
1178 static void ColouriseDDoc(
1179     unsigned startPos,
1180     int length,
1181     int initStyle,
1182     WordList * keywordlists[],
1183     Accessor & styler
1184     )
1185 {
1186     bool fold = styler.GetPropertyInt("fold");
1187     foldComments = styler.GetPropertyInt("fold.comment") != 0;
1188     bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
1189
1190     unsigned endPos = startPos + length;
1191     StatePtr state(new DefaultState);
1192
1193     // reconstruct the initial state
1194     int lineState = styler.GetLineState(styler.GetLine(startPos)) & 0xFFFFF;
1195     switch (initStyle)
1196     {
1197         case SCE_D_STRING:
1198         case SCE_D_STRING2:
1199             if ((lineState & 0xFF) == StringType::regular)
1200                 state = StatePtr(new DoubleQuotedString);
1201             else if ((lineState & 0xFF) == StringType::wysiwyg)
1202                 state = StatePtr(new WysiwygString(lineState >> 8));
1203             else if ((lineState & 0xFF) == StringType::hex)
1204                 state = StatePtr(new HexString(lineState >> 8));
1205             else if ((lineState & 0xFF) == StringType::delimited)
1206             {
1207                 // back-track to the start of the string
1208                 int firstQuote = (lineState >> 8);
1209                 while (startPos > 0)
1210                 {
1211                     int ch = PreviousChar(startPos, styler);
1212                     if (ch == '"' && --firstQuote == 0)
1213                         break;
1214                 }
1215                 PreviousChar(startPos, styler);
1216             }
1217             break;
1218         case SCE_D_STRING_ERROR:
1219             // string error can span beyond EOL only at the end of a delimited string
1220             state = StatePtr(new DelimitedStringEnd);
1221             break;
1222         case SCE_D_COMMENT:
1223         case SCE_D_DDOC:
1224         case SCE_D_DDOC_MACRO:
1225         case SCE_D_DDOC_MACRO_ERROR:
1226         {
1227             // back-track to the start of the comment
1228             int type = lineState & 0xFF;
1229             int toSkip = lineState >> 8;
1230             while (startPos > 0)
1231             {
1232                 int ch = PreviousChar(startPos, styler);
1233                 if (ch == type && --toSkip == 0)
1234                     break;
1235             }
1236             PreviousChar(startPos, styler);
1237             break;
1238         }
1239         default:
1240             state = StatePtr(new DefaultState);
1241     }
1242
1243     styler.StartAt(startPos);
1244     styler.StartSegment(startPos);
1245
1246     int line = styler.GetLine(startPos);
1247
1248     folder.init(styler.GetLineState(line) >> 20, foldCompact);
1249     bool emptyLine = true;
1250
1251     while (startPos < endPos)
1252     {
1253         unsigned nextPos = startPos;
1254
1255         int ch = (unsigned char) styler.SafeGetCharAt(nextPos++);
1256
1257         // test for EOL
1258         if (ch == '\r' || ch == '\n')
1259         {
1260             if (ch == '\r' && styler.SafeGetCharAt(nextPos) == '\n')
1261                 ++nextPos;
1262             ch = eol;
1263         }
1264
1265         // test for range ("..")
1266         else if (ch == '.' && styler.SafeGetCharAt(nextPos) == '.')
1267         {
1268             ++nextPos;
1269             ch = range;
1270         }
1271
1272         // test for shift-encodings
1273         else if (styler.IsLeadByte((char)ch))
1274             ch = (unsigned char) styler.SafeGetCharAt(nextPos++) | (ch << 8);
1275
1276         state = state->onChar(ch, Colourizer(styler, keywordlists, startPos, nextPos));
1277
1278         if (!(isspace(ch) || ch == eol))
1279             emptyLine = false;
1280
1281         startPos = nextPos;
1282
1283         // treat EOF like EOL
1284         if (startPos >= endPos)
1285         {
1286             ch = eol;
1287             state = state->onChar(ch, Colourizer(styler, keywordlists, endPos, endPos));
1288         }
1289
1290         if (ch == eol)
1291         {
1292             if (fold)
1293                 styler.SetLevel(line, folder.getLevel() | SC_FOLDLEVELHEADERFLAG * folder.isFoldingPoint() | SC_FOLDLEVELWHITEFLAG * emptyLine);
1294             folder.newLine();
1295             emptyLine = true;
1296             ++line;
1297             styler.SetLineState(line, (state->getSubStyle() & 0xFFFFF) | (folder.getNesting() << 20));
1298         }
1299     }
1300
1301     // if there's an unfinished delimited string, make sure the segment ends with
1302     // a different style than it was so that the string is re-coloured till its end.
1303     int style = state->getStyle();
1304     if (
1305         style == SCE_D_STRING
1306         && (
1307             (state->getSubStyle() & 0xFF) == StringType::delimited
1308             || (state->getSubStyle() & 0xFF) == StringType::hex
1309             )
1310         && styler.StyleAt(endPos-1) == SCE_D_STRING
1311         )
1312     {
1313         style = SCE_D_STRING2;
1314     }
1315     styler.ColourTo(endPos-1, style);
1316 }
1317
1318 static void FoldDDoc(
1319     unsigned startPos,
1320     int length,
1321     int initStyle,
1322     WordList * keywordlists[],
1323     Accessor & styler
1324     )
1325 {
1326 }
1327
1328 static char const * const keywordListsDesc[] =
1329 {
1330     "Keywords",
1331     0
1332 };
1333
1334 }
1335
1336 LexerModule lmD(SCLEX_D, &dlex::ColouriseDDoc, "d", &dlex::FoldDDoc, dlex::keywordListsDesc);
Note: See TracBrowser for help on using the browser.