Update Scintilla to version 3.7.1
[geany-mirror.git] / scintilla / lexers / LexCPP.cxx
blobec040fbf6ce47bda5c21a803a5f0bb3e7ae7fb15
1 // Scintilla source code edit control
2 /** @file LexCPP.cxx
3 ** Lexer for C++, C, Java, and JavaScript.
4 ** Further folding features and configuration properties added by "Udo Lechner" <dlchnr(at)gmx(dot)net>
5 **/
6 // Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
7 // The License.txt file describes the conditions under which this software may be distributed.
9 #include <stdlib.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <stdarg.h>
13 #include <assert.h>
14 #include <ctype.h>
16 #include <string>
17 #include <vector>
18 #include <map>
19 #include <algorithm>
21 #include "ILexer.h"
22 #include "Scintilla.h"
23 #include "SciLexer.h"
25 #include "WordList.h"
26 #include "LexAccessor.h"
27 #include "Accessor.h"
28 #include "StyleContext.h"
29 #include "CharacterSet.h"
30 #include "LexerModule.h"
31 #include "OptionSet.h"
32 #include "SparseState.h"
33 #include "SubStyles.h"
35 #ifdef SCI_NAMESPACE
36 using namespace Scintilla;
37 #endif
39 namespace {
40 // Use an unnamed namespace to protect the functions and classes from name conflicts
42 bool IsSpaceEquiv(int state) {
43 return (state <= SCE_C_COMMENTDOC) ||
44 // including SCE_C_DEFAULT, SCE_C_COMMENT, SCE_C_COMMENTLINE
45 (state == SCE_C_COMMENTLINEDOC) || (state == SCE_C_COMMENTDOCKEYWORD) ||
46 (state == SCE_C_COMMENTDOCKEYWORDERROR);
49 // Preconditions: sc.currentPos points to a character after '+' or '-'.
50 // The test for pos reaching 0 should be redundant,
51 // and is in only for safety measures.
52 // Limitation: this code will give the incorrect answer for code like
53 // a = b+++/ptn/...
54 // Putting a space between the '++' post-inc operator and the '+' binary op
55 // fixes this, and is highly recommended for readability anyway.
56 bool FollowsPostfixOperator(StyleContext &sc, LexAccessor &styler) {
57 Sci_Position pos = (Sci_Position) sc.currentPos;
58 while (--pos > 0) {
59 char ch = styler[pos];
60 if (ch == '+' || ch == '-') {
61 return styler[pos - 1] == ch;
64 return false;
67 bool followsReturnKeyword(StyleContext &sc, LexAccessor &styler) {
68 // Don't look at styles, so no need to flush.
69 Sci_Position pos = (Sci_Position) sc.currentPos;
70 Sci_Position currentLine = styler.GetLine(pos);
71 Sci_Position lineStartPos = styler.LineStart(currentLine);
72 while (--pos > lineStartPos) {
73 char ch = styler.SafeGetCharAt(pos);
74 if (ch != ' ' && ch != '\t') {
75 break;
78 const char *retBack = "nruter";
79 const char *s = retBack;
80 while (*s
81 && pos >= lineStartPos
82 && styler.SafeGetCharAt(pos) == *s) {
83 s++;
84 pos--;
86 return !*s;
89 bool IsSpaceOrTab(int ch) {
90 return ch == ' ' || ch == '\t';
93 bool OnlySpaceOrTab(const std::string &s) {
94 for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) {
95 if (!IsSpaceOrTab(*it))
96 return false;
98 return true;
101 std::vector<std::string> StringSplit(const std::string &text, int separator) {
102 std::vector<std::string> vs(text.empty() ? 0 : 1);
103 for (std::string::const_iterator it = text.begin(); it != text.end(); ++it) {
104 if (*it == separator) {
105 vs.push_back(std::string());
106 } else {
107 vs.back() += *it;
110 return vs;
113 struct BracketPair {
114 std::vector<std::string>::iterator itBracket;
115 std::vector<std::string>::iterator itEndBracket;
118 BracketPair FindBracketPair(std::vector<std::string> &tokens) {
119 BracketPair bp;
120 std::vector<std::string>::iterator itTok = std::find(tokens.begin(), tokens.end(), "(");
121 bp.itBracket = tokens.end();
122 bp.itEndBracket = tokens.end();
123 if (itTok != tokens.end()) {
124 bp.itBracket = itTok;
125 size_t nest = 0;
126 while (itTok != tokens.end()) {
127 if (*itTok == "(") {
128 nest++;
129 } else if (*itTok == ")") {
130 nest--;
131 if (nest == 0) {
132 bp.itEndBracket = itTok;
133 return bp;
136 ++itTok;
139 bp.itBracket = tokens.end();
140 return bp;
143 void highlightTaskMarker(StyleContext &sc, LexAccessor &styler,
144 int activity, WordList &markerList, bool caseSensitive){
145 if ((isoperator(sc.chPrev) || IsASpace(sc.chPrev)) && markerList.Length()) {
146 const int lengthMarker = 50;
147 char marker[lengthMarker+1];
148 Sci_Position currPos = (Sci_Position) sc.currentPos;
149 int i = 0;
150 while (i < lengthMarker) {
151 char ch = styler.SafeGetCharAt(currPos + i);
152 if (IsASpace(ch) || isoperator(ch)) {
153 break;
155 if (caseSensitive)
156 marker[i] = ch;
157 else
158 marker[i] = static_cast<char>(tolower(ch));
159 i++;
161 marker[i] = '\0';
162 if (markerList.InList(marker)) {
163 sc.SetState(SCE_C_TASKMARKER|activity);
168 struct EscapeSequence {
169 int digitsLeft;
170 CharacterSet setHexDigits;
171 CharacterSet setOctDigits;
172 CharacterSet setNoneNumeric;
173 CharacterSet *escapeSetValid;
174 EscapeSequence() {
175 digitsLeft = 0;
176 escapeSetValid = 0;
177 setHexDigits = CharacterSet(CharacterSet::setDigits, "ABCDEFabcdef");
178 setOctDigits = CharacterSet(CharacterSet::setNone, "01234567");
180 void resetEscapeState(int nextChar) {
181 digitsLeft = 0;
182 escapeSetValid = &setNoneNumeric;
183 if (nextChar == 'U') {
184 digitsLeft = 9;
185 escapeSetValid = &setHexDigits;
186 } else if (nextChar == 'u') {
187 digitsLeft = 5;
188 escapeSetValid = &setHexDigits;
189 } else if (nextChar == 'x') {
190 digitsLeft = 5;
191 escapeSetValid = &setHexDigits;
192 } else if (setOctDigits.Contains(nextChar)) {
193 digitsLeft = 3;
194 escapeSetValid = &setOctDigits;
197 bool atEscapeEnd(int currChar) const {
198 return (digitsLeft <= 0) || !escapeSetValid->Contains(currChar);
202 std::string GetRestOfLine(LexAccessor &styler, Sci_Position start, bool allowSpace) {
203 std::string restOfLine;
204 Sci_Position i =0;
205 char ch = styler.SafeGetCharAt(start, '\n');
206 Sci_Position endLine = styler.LineEnd(styler.GetLine(start));
207 while (((start+i) < endLine) && (ch != '\r')) {
208 char chNext = styler.SafeGetCharAt(start + i + 1, '\n');
209 if (ch == '/' && (chNext == '/' || chNext == '*'))
210 break;
211 if (allowSpace || (ch != ' '))
212 restOfLine += ch;
213 i++;
214 ch = chNext;
216 return restOfLine;
219 bool IsStreamCommentStyle(int style) {
220 return style == SCE_C_COMMENT ||
221 style == SCE_C_COMMENTDOC ||
222 style == SCE_C_COMMENTDOCKEYWORD ||
223 style == SCE_C_COMMENTDOCKEYWORDERROR;
226 struct PPDefinition {
227 Sci_Position line;
228 std::string key;
229 std::string value;
230 bool isUndef;
231 std::string arguments;
232 PPDefinition(Sci_Position line_, const std::string &key_, const std::string &value_, bool isUndef_ = false, const std::string &arguments_="") :
233 line(line_), key(key_), value(value_), isUndef(isUndef_), arguments(arguments_) {
237 class LinePPState {
238 int state;
239 int ifTaken;
240 int level;
241 bool ValidLevel() const {
242 return level >= 0 && level < 32;
244 int maskLevel() const {
245 return 1 << level;
247 public:
248 LinePPState() : state(0), ifTaken(0), level(-1) {
250 bool IsInactive() const {
251 return state != 0;
253 bool CurrentIfTaken() const {
254 return (ifTaken & maskLevel()) != 0;
256 void StartSection(bool on) {
257 level++;
258 if (ValidLevel()) {
259 if (on) {
260 state &= ~maskLevel();
261 ifTaken |= maskLevel();
262 } else {
263 state |= maskLevel();
264 ifTaken &= ~maskLevel();
268 void EndSection() {
269 if (ValidLevel()) {
270 state &= ~maskLevel();
271 ifTaken &= ~maskLevel();
273 level--;
275 void InvertCurrentLevel() {
276 if (ValidLevel()) {
277 state ^= maskLevel();
278 ifTaken |= maskLevel();
283 // Hold the preprocessor state for each line seen.
284 // Currently one entry per line but could become sparse with just one entry per preprocessor line.
285 class PPStates {
286 std::vector<LinePPState> vlls;
287 public:
288 LinePPState ForLine(Sci_Position line) const {
289 if ((line > 0) && (vlls.size() > static_cast<size_t>(line))) {
290 return vlls[line];
291 } else {
292 return LinePPState();
295 void Add(Sci_Position line, LinePPState lls) {
296 vlls.resize(line+1);
297 vlls[line] = lls;
301 // An individual named option for use in an OptionSet
303 // Options used for LexerCPP
304 struct OptionsCPP {
305 bool stylingWithinPreprocessor;
306 bool identifiersAllowDollars;
307 bool trackPreprocessor;
308 bool updatePreprocessor;
309 bool verbatimStringsAllowEscapes;
310 bool triplequotedStrings;
311 bool hashquotedStrings;
312 bool backQuotedStrings;
313 bool escapeSequence;
314 bool fold;
315 bool foldSyntaxBased;
316 bool foldComment;
317 bool foldCommentMultiline;
318 bool foldCommentExplicit;
319 std::string foldExplicitStart;
320 std::string foldExplicitEnd;
321 bool foldExplicitAnywhere;
322 bool foldPreprocessor;
323 bool foldPreprocessorAtElse;
324 bool foldCompact;
325 bool foldAtElse;
326 OptionsCPP() {
327 stylingWithinPreprocessor = false;
328 identifiersAllowDollars = true;
329 trackPreprocessor = true;
330 updatePreprocessor = true;
331 verbatimStringsAllowEscapes = false;
332 triplequotedStrings = false;
333 hashquotedStrings = false;
334 backQuotedStrings = false;
335 escapeSequence = false;
336 fold = false;
337 foldSyntaxBased = true;
338 foldComment = false;
339 foldCommentMultiline = true;
340 foldCommentExplicit = true;
341 foldExplicitStart = "";
342 foldExplicitEnd = "";
343 foldExplicitAnywhere = false;
344 foldPreprocessor = false;
345 foldPreprocessorAtElse = false;
346 foldCompact = false;
347 foldAtElse = false;
351 const char *const cppWordLists[] = {
352 "Primary keywords and identifiers",
353 "Secondary keywords and identifiers",
354 "Documentation comment keywords",
355 "Global classes and typedefs",
356 "Preprocessor definitions",
357 "Task marker and error marker keywords",
361 struct OptionSetCPP : public OptionSet<OptionsCPP> {
362 OptionSetCPP() {
363 DefineProperty("styling.within.preprocessor", &OptionsCPP::stylingWithinPreprocessor,
364 "For C++ code, determines whether all preprocessor code is styled in the "
365 "preprocessor style (0, the default) or only from the initial # to the end "
366 "of the command word(1).");
368 DefineProperty("lexer.cpp.allow.dollars", &OptionsCPP::identifiersAllowDollars,
369 "Set to 0 to disallow the '$' character in identifiers with the cpp lexer.");
371 DefineProperty("lexer.cpp.track.preprocessor", &OptionsCPP::trackPreprocessor,
372 "Set to 1 to interpret #if/#else/#endif to grey out code that is not active.");
374 DefineProperty("lexer.cpp.update.preprocessor", &OptionsCPP::updatePreprocessor,
375 "Set to 1 to update preprocessor definitions when #define found.");
377 DefineProperty("lexer.cpp.verbatim.strings.allow.escapes", &OptionsCPP::verbatimStringsAllowEscapes,
378 "Set to 1 to allow verbatim strings to contain escape sequences.");
380 DefineProperty("lexer.cpp.triplequoted.strings", &OptionsCPP::triplequotedStrings,
381 "Set to 1 to enable highlighting of triple-quoted strings.");
383 DefineProperty("lexer.cpp.hashquoted.strings", &OptionsCPP::hashquotedStrings,
384 "Set to 1 to enable highlighting of hash-quoted strings.");
386 DefineProperty("lexer.cpp.backquoted.strings", &OptionsCPP::backQuotedStrings,
387 "Set to 1 to enable highlighting of back-quoted raw strings .");
389 DefineProperty("lexer.cpp.escape.sequence", &OptionsCPP::escapeSequence,
390 "Set to 1 to enable highlighting of escape sequences in strings");
392 DefineProperty("fold", &OptionsCPP::fold);
394 DefineProperty("fold.cpp.syntax.based", &OptionsCPP::foldSyntaxBased,
395 "Set this property to 0 to disable syntax based folding.");
397 DefineProperty("fold.comment", &OptionsCPP::foldComment,
398 "This option enables folding multi-line comments and explicit fold points when using the C++ lexer. "
399 "Explicit fold points allows adding extra folding by placing a //{ comment at the start and a //} "
400 "at the end of a section that should fold.");
402 DefineProperty("fold.cpp.comment.multiline", &OptionsCPP::foldCommentMultiline,
403 "Set this property to 0 to disable folding multi-line comments when fold.comment=1.");
405 DefineProperty("fold.cpp.comment.explicit", &OptionsCPP::foldCommentExplicit,
406 "Set this property to 0 to disable folding explicit fold points when fold.comment=1.");
408 DefineProperty("fold.cpp.explicit.start", &OptionsCPP::foldExplicitStart,
409 "The string to use for explicit fold start points, replacing the standard //{.");
411 DefineProperty("fold.cpp.explicit.end", &OptionsCPP::foldExplicitEnd,
412 "The string to use for explicit fold end points, replacing the standard //}.");
414 DefineProperty("fold.cpp.explicit.anywhere", &OptionsCPP::foldExplicitAnywhere,
415 "Set this property to 1 to enable explicit fold points anywhere, not just in line comments.");
417 DefineProperty("fold.cpp.preprocessor.at.else", &OptionsCPP::foldPreprocessorAtElse,
418 "This option enables folding on a preprocessor #else or #endif line of an #if statement.");
420 DefineProperty("fold.preprocessor", &OptionsCPP::foldPreprocessor,
421 "This option enables folding preprocessor directives when using the C++ lexer. "
422 "Includes C#'s explicit #region and #endregion folding directives.");
424 DefineProperty("fold.compact", &OptionsCPP::foldCompact);
426 DefineProperty("fold.at.else", &OptionsCPP::foldAtElse,
427 "This option enables C++ folding on a \"} else {\" line of an if statement.");
429 DefineWordListSets(cppWordLists);
433 const char styleSubable[] = {SCE_C_IDENTIFIER, SCE_C_COMMENTDOCKEYWORD, 0};
437 class LexerCPP : public ILexerWithSubStyles {
438 bool caseSensitive;
439 CharacterSet setWord;
440 CharacterSet setNegationOp;
441 CharacterSet setArithmethicOp;
442 CharacterSet setRelOp;
443 CharacterSet setLogicalOp;
444 CharacterSet setWordStart;
445 PPStates vlls;
446 std::vector<PPDefinition> ppDefineHistory;
447 WordList keywords;
448 WordList keywords2;
449 WordList keywords3;
450 WordList keywords4;
451 WordList ppDefinitions;
452 WordList markerList;
453 struct SymbolValue {
454 std::string value;
455 std::string arguments;
456 SymbolValue(const std::string &value_="", const std::string &arguments_="") : value(value_), arguments(arguments_) {
458 SymbolValue &operator = (const std::string &value_) {
459 value = value_;
460 arguments.clear();
461 return *this;
463 bool IsMacro() const {
464 return !arguments.empty();
467 typedef std::map<std::string, SymbolValue> SymbolTable;
468 SymbolTable preprocessorDefinitionsStart;
469 OptionsCPP options;
470 OptionSetCPP osCPP;
471 EscapeSequence escapeSeq;
472 SparseState<std::string> rawStringTerminators;
473 enum { activeFlag = 0x40 };
474 enum { ssIdentifier, ssDocKeyword };
475 SubStyles subStyles;
476 public:
477 explicit LexerCPP(bool caseSensitive_) :
478 caseSensitive(caseSensitive_),
479 setWord(CharacterSet::setAlphaNum, "._", 0x80, true),
480 setNegationOp(CharacterSet::setNone, "!"),
481 setArithmethicOp(CharacterSet::setNone, "+-/*%"),
482 setRelOp(CharacterSet::setNone, "=!<>"),
483 setLogicalOp(CharacterSet::setNone, "|&"),
484 subStyles(styleSubable, 0x80, 0x40, activeFlag) {
486 virtual ~LexerCPP() {
488 void SCI_METHOD Release() {
489 delete this;
491 int SCI_METHOD Version() const {
492 return lvSubStyles;
494 const char * SCI_METHOD PropertyNames() {
495 return osCPP.PropertyNames();
497 int SCI_METHOD PropertyType(const char *name) {
498 return osCPP.PropertyType(name);
500 const char * SCI_METHOD DescribeProperty(const char *name) {
501 return osCPP.DescribeProperty(name);
503 Sci_Position SCI_METHOD PropertySet(const char *key, const char *val);
504 const char * SCI_METHOD DescribeWordListSets() {
505 return osCPP.DescribeWordListSets();
507 Sci_Position SCI_METHOD WordListSet(int n, const char *wl);
508 void SCI_METHOD Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess);
509 void SCI_METHOD Fold(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess);
511 void * SCI_METHOD PrivateCall(int, void *) {
512 return 0;
515 int SCI_METHOD LineEndTypesSupported() {
516 return SC_LINE_END_TYPE_UNICODE;
519 int SCI_METHOD AllocateSubStyles(int styleBase, int numberStyles) {
520 return subStyles.Allocate(styleBase, numberStyles);
522 int SCI_METHOD SubStylesStart(int styleBase) {
523 return subStyles.Start(styleBase);
525 int SCI_METHOD SubStylesLength(int styleBase) {
526 return subStyles.Length(styleBase);
528 int SCI_METHOD StyleFromSubStyle(int subStyle) {
529 int styleBase = subStyles.BaseStyle(MaskActive(subStyle));
530 int active = subStyle & activeFlag;
531 return styleBase | active;
533 int SCI_METHOD PrimaryStyleFromStyle(int style) {
534 return MaskActive(style);
536 void SCI_METHOD FreeSubStyles() {
537 subStyles.Free();
539 void SCI_METHOD SetIdentifiers(int style, const char *identifiers) {
540 subStyles.SetIdentifiers(style, identifiers);
542 int SCI_METHOD DistanceToSecondaryStyles() {
543 return activeFlag;
545 const char * SCI_METHOD GetSubStyleBases() {
546 return styleSubable;
549 static ILexer *LexerFactoryCPP() {
550 return new LexerCPP(true);
552 static ILexer *LexerFactoryCPPInsensitive() {
553 return new LexerCPP(false);
555 static int MaskActive(int style) {
556 return style & ~activeFlag;
558 void EvaluateTokens(std::vector<std::string> &tokens, const SymbolTable &preprocessorDefinitions);
559 std::vector<std::string> Tokenize(const std::string &expr) const;
560 bool EvaluateExpression(const std::string &expr, const SymbolTable &preprocessorDefinitions);
563 Sci_Position SCI_METHOD LexerCPP::PropertySet(const char *key, const char *val) {
564 if (osCPP.PropertySet(&options, key, val)) {
565 if (strcmp(key, "lexer.cpp.allow.dollars") == 0) {
566 setWord = CharacterSet(CharacterSet::setAlphaNum, "._", 0x80, true);
567 if (options.identifiersAllowDollars) {
568 setWord.Add('$');
571 return 0;
573 return -1;
576 Sci_Position SCI_METHOD LexerCPP::WordListSet(int n, const char *wl) {
577 WordList *wordListN = 0;
578 switch (n) {
579 case 0:
580 wordListN = &keywords;
581 break;
582 case 1:
583 wordListN = &keywords2;
584 break;
585 case 2:
586 wordListN = &keywords3;
587 break;
588 case 3:
589 wordListN = &keywords4;
590 break;
591 case 4:
592 wordListN = &ppDefinitions;
593 break;
594 case 5:
595 wordListN = &markerList;
596 break;
598 Sci_Position firstModification = -1;
599 if (wordListN) {
600 WordList wlNew;
601 wlNew.Set(wl);
602 if (*wordListN != wlNew) {
603 wordListN->Set(wl);
604 firstModification = 0;
605 if (n == 4) {
606 // Rebuild preprocessorDefinitions
607 preprocessorDefinitionsStart.clear();
608 for (int nDefinition = 0; nDefinition < ppDefinitions.Length(); nDefinition++) {
609 const char *cpDefinition = ppDefinitions.WordAt(nDefinition);
610 const char *cpEquals = strchr(cpDefinition, '=');
611 if (cpEquals) {
612 std::string name(cpDefinition, cpEquals - cpDefinition);
613 std::string val(cpEquals+1);
614 size_t bracket = name.find('(');
615 size_t bracketEnd = name.find(')');
616 if ((bracket != std::string::npos) && (bracketEnd != std::string::npos)) {
617 // Macro
618 std::string args = name.substr(bracket + 1, bracketEnd - bracket - 1);
619 name = name.substr(0, bracket);
620 preprocessorDefinitionsStart[name] = SymbolValue(val, args);
621 } else {
622 preprocessorDefinitionsStart[name] = val;
624 } else {
625 std::string name(cpDefinition);
626 std::string val("1");
627 preprocessorDefinitionsStart[name] = val;
633 return firstModification;
636 // Functor used to truncate history
637 struct After {
638 Sci_Position line;
639 explicit After(Sci_Position line_) : line(line_) {}
640 bool operator()(PPDefinition &p) const {
641 return p.line > line;
645 void SCI_METHOD LexerCPP::Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) {
646 LexAccessor styler(pAccess);
648 CharacterSet setOKBeforeRE(CharacterSet::setNone, "([{=,:;!%^&*|?~+-");
649 CharacterSet setCouldBePostOp(CharacterSet::setNone, "+-");
651 CharacterSet setDoxygen(CharacterSet::setAlpha, "$@\\&<>#{}[]");
653 setWordStart = CharacterSet(CharacterSet::setAlpha, "_", 0x80, true);
655 CharacterSet setInvalidRawFirst(CharacterSet::setNone, " )\\\t\v\f\n");
657 if (options.identifiersAllowDollars) {
658 setWordStart.Add('$');
661 int chPrevNonWhite = ' ';
662 int visibleChars = 0;
663 bool lastWordWasUUID = false;
664 int styleBeforeDCKeyword = SCE_C_DEFAULT;
665 int styleBeforeTaskMarker = SCE_C_DEFAULT;
666 bool continuationLine = false;
667 bool isIncludePreprocessor = false;
668 bool isStringInPreprocessor = false;
669 bool inRERange = false;
670 bool seenDocKeyBrace = false;
672 Sci_Position lineCurrent = styler.GetLine(startPos);
673 if ((MaskActive(initStyle) == SCE_C_PREPROCESSOR) ||
674 (MaskActive(initStyle) == SCE_C_COMMENTLINE) ||
675 (MaskActive(initStyle) == SCE_C_COMMENTLINEDOC)) {
676 // Set continuationLine if last character of previous line is '\'
677 if (lineCurrent > 0) {
678 Sci_Position endLinePrevious = styler.LineEnd(lineCurrent - 1);
679 if (endLinePrevious > 0) {
680 continuationLine = styler.SafeGetCharAt(endLinePrevious-1) == '\\';
685 // look back to set chPrevNonWhite properly for better regex colouring
686 if (startPos > 0) {
687 Sci_Position back = startPos;
688 while (--back && IsSpaceEquiv(MaskActive(styler.StyleAt(back))))
690 if (MaskActive(styler.StyleAt(back)) == SCE_C_OPERATOR) {
691 chPrevNonWhite = styler.SafeGetCharAt(back);
695 StyleContext sc(startPos, length, initStyle, styler);
696 LinePPState preproc = vlls.ForLine(lineCurrent);
698 bool definitionsChanged = false;
700 // Truncate ppDefineHistory before current line
702 if (!options.updatePreprocessor)
703 ppDefineHistory.clear();
705 std::vector<PPDefinition>::iterator itInvalid = std::find_if(ppDefineHistory.begin(), ppDefineHistory.end(), After(lineCurrent-1));
706 if (itInvalid != ppDefineHistory.end()) {
707 ppDefineHistory.erase(itInvalid, ppDefineHistory.end());
708 definitionsChanged = true;
711 SymbolTable preprocessorDefinitions = preprocessorDefinitionsStart;
712 for (std::vector<PPDefinition>::iterator itDef = ppDefineHistory.begin(); itDef != ppDefineHistory.end(); ++itDef) {
713 if (itDef->isUndef)
714 preprocessorDefinitions.erase(itDef->key);
715 else
716 preprocessorDefinitions[itDef->key] = SymbolValue(itDef->value, itDef->arguments);
719 std::string rawStringTerminator = rawStringTerminators.ValueAt(lineCurrent-1);
720 SparseState<std::string> rawSTNew(lineCurrent);
722 int activitySet = preproc.IsInactive() ? activeFlag : 0;
724 const WordClassifier &classifierIdentifiers = subStyles.Classifier(SCE_C_IDENTIFIER);
725 const WordClassifier &classifierDocKeyWords = subStyles.Classifier(SCE_C_COMMENTDOCKEYWORD);
727 Sci_Position lineEndNext = styler.LineEnd(lineCurrent);
729 for (; sc.More();) {
731 if (sc.atLineStart) {
732 // Using MaskActive() is not needed in the following statement.
733 // Inside inactive preprocessor declaration, state will be reset anyway at the end of this block.
734 if ((sc.state == SCE_C_STRING) || (sc.state == SCE_C_CHARACTER)) {
735 // Prevent SCE_C_STRINGEOL from leaking back to previous line which
736 // ends with a line continuation by locking in the state up to this position.
737 sc.SetState(sc.state);
739 if ((MaskActive(sc.state) == SCE_C_PREPROCESSOR) && (!continuationLine)) {
740 sc.SetState(SCE_C_DEFAULT|activitySet);
742 // Reset states to beginning of colourise so no surprises
743 // if different sets of lines lexed.
744 visibleChars = 0;
745 lastWordWasUUID = false;
746 isIncludePreprocessor = false;
747 inRERange = false;
748 if (preproc.IsInactive()) {
749 activitySet = activeFlag;
750 sc.SetState(sc.state | activitySet);
754 if (sc.atLineEnd) {
755 lineCurrent++;
756 lineEndNext = styler.LineEnd(lineCurrent);
757 vlls.Add(lineCurrent, preproc);
758 if (rawStringTerminator != "") {
759 rawSTNew.Set(lineCurrent-1, rawStringTerminator);
763 // Handle line continuation generically.
764 if (sc.ch == '\\') {
765 if (static_cast<Sci_Position>((sc.currentPos+1)) >= lineEndNext) {
766 lineCurrent++;
767 lineEndNext = styler.LineEnd(lineCurrent);
768 vlls.Add(lineCurrent, preproc);
769 if (rawStringTerminator != "") {
770 rawSTNew.Set(lineCurrent-1, rawStringTerminator);
772 sc.Forward();
773 if (sc.ch == '\r' && sc.chNext == '\n') {
774 // Even in UTF-8, \r and \n are separate
775 sc.Forward();
777 continuationLine = true;
778 sc.Forward();
779 continue;
783 const bool atLineEndBeforeSwitch = sc.atLineEnd;
785 // Determine if the current state should terminate.
786 switch (MaskActive(sc.state)) {
787 case SCE_C_OPERATOR:
788 sc.SetState(SCE_C_DEFAULT|activitySet);
789 break;
790 case SCE_C_NUMBER:
791 // We accept almost anything because of hex. and number suffixes
792 if (sc.ch == '_') {
793 sc.ChangeState(SCE_C_USERLITERAL|activitySet);
794 } else if (!(setWord.Contains(sc.ch)
795 || (sc.ch == '\'')
796 || ((sc.ch == '+' || sc.ch == '-') && (sc.chPrev == 'e' || sc.chPrev == 'E' ||
797 sc.chPrev == 'p' || sc.chPrev == 'P')))) {
798 sc.SetState(SCE_C_DEFAULT|activitySet);
800 break;
801 case SCE_C_USERLITERAL:
802 if (!(setWord.Contains(sc.ch)))
803 sc.SetState(SCE_C_DEFAULT|activitySet);
804 break;
805 case SCE_C_IDENTIFIER:
806 if (sc.atLineStart || sc.atLineEnd || !setWord.Contains(sc.ch) || (sc.ch == '.')) {
807 char s[1000];
808 if (caseSensitive) {
809 sc.GetCurrent(s, sizeof(s));
810 } else {
811 sc.GetCurrentLowered(s, sizeof(s));
813 if (keywords.InList(s)) {
814 lastWordWasUUID = strcmp(s, "uuid") == 0;
815 sc.ChangeState(SCE_C_WORD|activitySet);
816 } else if (keywords2.InList(s)) {
817 sc.ChangeState(SCE_C_WORD2|activitySet);
818 } else if (keywords4.InList(s)) {
819 sc.ChangeState(SCE_C_GLOBALCLASS|activitySet);
820 } else {
821 int subStyle = classifierIdentifiers.ValueFor(s);
822 if (subStyle >= 0) {
823 sc.ChangeState(subStyle|activitySet);
826 const bool literalString = sc.ch == '\"';
827 if (literalString || sc.ch == '\'') {
828 size_t lenS = strlen(s);
829 const bool raw = literalString && sc.chPrev == 'R' && !setInvalidRawFirst.Contains(sc.chNext);
830 if (raw)
831 s[lenS--] = '\0';
832 bool valid =
833 (lenS == 0) ||
834 ((lenS == 1) && ((s[0] == 'L') || (s[0] == 'u') || (s[0] == 'U'))) ||
835 ((lenS == 2) && literalString && (s[0] == 'u') && (s[1] == '8'));
836 if (valid) {
837 if (literalString) {
838 if (raw) {
839 // Set the style of the string prefix to SCE_C_STRINGRAW but then change to
840 // SCE_C_DEFAULT as that allows the raw string start code to run.
841 sc.ChangeState(SCE_C_STRINGRAW|activitySet);
842 sc.SetState(SCE_C_DEFAULT|activitySet);
843 } else {
844 sc.ChangeState(SCE_C_STRING|activitySet);
846 } else {
847 sc.ChangeState(SCE_C_CHARACTER|activitySet);
849 } else {
850 sc.SetState(SCE_C_DEFAULT | activitySet);
852 } else {
853 sc.SetState(SCE_C_DEFAULT|activitySet);
856 break;
857 case SCE_C_PREPROCESSOR:
858 if (options.stylingWithinPreprocessor) {
859 if (IsASpace(sc.ch)) {
860 sc.SetState(SCE_C_DEFAULT|activitySet);
862 } else if (isStringInPreprocessor && (sc.Match('>') || sc.Match('\"') || sc.atLineEnd)) {
863 isStringInPreprocessor = false;
864 } else if (!isStringInPreprocessor) {
865 if ((isIncludePreprocessor && sc.Match('<')) || sc.Match('\"')) {
866 isStringInPreprocessor = true;
867 } else if (sc.Match('/', '*')) {
868 if (sc.Match("/**") || sc.Match("/*!")) {
869 sc.SetState(SCE_C_PREPROCESSORCOMMENTDOC|activitySet);
870 } else {
871 sc.SetState(SCE_C_PREPROCESSORCOMMENT|activitySet);
873 sc.Forward(); // Eat the *
874 } else if (sc.Match('/', '/')) {
875 sc.SetState(SCE_C_DEFAULT|activitySet);
878 break;
879 case SCE_C_PREPROCESSORCOMMENT:
880 case SCE_C_PREPROCESSORCOMMENTDOC:
881 if (sc.Match('*', '/')) {
882 sc.Forward();
883 sc.ForwardSetState(SCE_C_PREPROCESSOR|activitySet);
884 continue; // Without advancing in case of '\'.
886 break;
887 case SCE_C_COMMENT:
888 if (sc.Match('*', '/')) {
889 sc.Forward();
890 sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
891 } else {
892 styleBeforeTaskMarker = SCE_C_COMMENT;
893 highlightTaskMarker(sc, styler, activitySet, markerList, caseSensitive);
895 break;
896 case SCE_C_COMMENTDOC:
897 if (sc.Match('*', '/')) {
898 sc.Forward();
899 sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
900 } else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support
901 // Verify that we have the conditions to mark a comment-doc-keyword
902 if ((IsASpace(sc.chPrev) || sc.chPrev == '*') && (!IsASpace(sc.chNext))) {
903 styleBeforeDCKeyword = SCE_C_COMMENTDOC;
904 sc.SetState(SCE_C_COMMENTDOCKEYWORD|activitySet);
907 break;
908 case SCE_C_COMMENTLINE:
909 if (sc.atLineStart && !continuationLine) {
910 sc.SetState(SCE_C_DEFAULT|activitySet);
911 } else {
912 styleBeforeTaskMarker = SCE_C_COMMENTLINE;
913 highlightTaskMarker(sc, styler, activitySet, markerList, caseSensitive);
915 break;
916 case SCE_C_COMMENTLINEDOC:
917 if (sc.atLineStart && !continuationLine) {
918 sc.SetState(SCE_C_DEFAULT|activitySet);
919 } else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support
920 // Verify that we have the conditions to mark a comment-doc-keyword
921 if ((IsASpace(sc.chPrev) || sc.chPrev == '/' || sc.chPrev == '!') && (!IsASpace(sc.chNext))) {
922 styleBeforeDCKeyword = SCE_C_COMMENTLINEDOC;
923 sc.SetState(SCE_C_COMMENTDOCKEYWORD|activitySet);
926 break;
927 case SCE_C_COMMENTDOCKEYWORD:
928 if ((styleBeforeDCKeyword == SCE_C_COMMENTDOC) && sc.Match('*', '/')) {
929 sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR);
930 sc.Forward();
931 sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
932 seenDocKeyBrace = false;
933 } else if (sc.ch == '[' || sc.ch == '{') {
934 seenDocKeyBrace = true;
935 } else if (!setDoxygen.Contains(sc.ch)
936 && !(seenDocKeyBrace && (sc.ch == ',' || sc.ch == '.'))) {
937 char s[100];
938 if (caseSensitive) {
939 sc.GetCurrent(s, sizeof(s));
940 } else {
941 sc.GetCurrentLowered(s, sizeof(s));
943 if (!(IsASpace(sc.ch) || (sc.ch == 0))) {
944 sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR|activitySet);
945 } else if (!keywords3.InList(s + 1)) {
946 int subStyleCDKW = classifierDocKeyWords.ValueFor(s+1);
947 if (subStyleCDKW >= 0) {
948 sc.ChangeState(subStyleCDKW|activitySet);
949 } else {
950 sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR|activitySet);
953 sc.SetState(styleBeforeDCKeyword|activitySet);
954 seenDocKeyBrace = false;
956 break;
957 case SCE_C_STRING:
958 if (sc.atLineEnd) {
959 sc.ChangeState(SCE_C_STRINGEOL|activitySet);
960 } else if (isIncludePreprocessor) {
961 if (sc.ch == '>') {
962 sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
963 isIncludePreprocessor = false;
965 } else if (sc.ch == '\\') {
966 if (options.escapeSequence) {
967 sc.SetState(SCE_C_ESCAPESEQUENCE|activitySet);
968 escapeSeq.resetEscapeState(sc.chNext);
970 sc.Forward(); // Skip all characters after the backslash
971 } else if (sc.ch == '\"') {
972 if (sc.chNext == '_') {
973 sc.ChangeState(SCE_C_USERLITERAL|activitySet);
974 } else {
975 sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
978 break;
979 case SCE_C_ESCAPESEQUENCE:
980 escapeSeq.digitsLeft--;
981 if (!escapeSeq.atEscapeEnd(sc.ch)) {
982 break;
984 if (sc.ch == '"') {
985 sc.SetState(SCE_C_STRING|activitySet);
986 sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
987 } else if (sc.ch == '\\') {
988 escapeSeq.resetEscapeState(sc.chNext);
989 sc.Forward();
990 } else {
991 sc.SetState(SCE_C_STRING|activitySet);
992 if (sc.atLineEnd) {
993 sc.ChangeState(SCE_C_STRINGEOL|activitySet);
996 break;
997 case SCE_C_HASHQUOTEDSTRING:
998 if (sc.ch == '\\') {
999 if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
1000 sc.Forward();
1002 } else if (sc.ch == '\"') {
1003 sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
1005 break;
1006 case SCE_C_STRINGRAW:
1007 if (sc.Match(rawStringTerminator.c_str())) {
1008 for (size_t termPos=rawStringTerminator.size(); termPos; termPos--)
1009 sc.Forward();
1010 sc.SetState(SCE_C_DEFAULT|activitySet);
1011 rawStringTerminator = "";
1013 break;
1014 case SCE_C_CHARACTER:
1015 if (sc.atLineEnd) {
1016 sc.ChangeState(SCE_C_STRINGEOL|activitySet);
1017 } else if (sc.ch == '\\') {
1018 if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
1019 sc.Forward();
1021 } else if (sc.ch == '\'') {
1022 if (sc.chNext == '_') {
1023 sc.ChangeState(SCE_C_USERLITERAL|activitySet);
1024 } else {
1025 sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
1028 break;
1029 case SCE_C_REGEX:
1030 if (sc.atLineStart) {
1031 sc.SetState(SCE_C_DEFAULT|activitySet);
1032 } else if (! inRERange && sc.ch == '/') {
1033 sc.Forward();
1034 while ((sc.ch < 0x80) && islower(sc.ch))
1035 sc.Forward(); // gobble regex flags
1036 sc.SetState(SCE_C_DEFAULT|activitySet);
1037 } else if (sc.ch == '\\' && (static_cast<Sci_Position>(sc.currentPos+1) < lineEndNext)) {
1038 // Gobble up the escaped character
1039 sc.Forward();
1040 } else if (sc.ch == '[') {
1041 inRERange = true;
1042 } else if (sc.ch == ']') {
1043 inRERange = false;
1045 break;
1046 case SCE_C_STRINGEOL:
1047 if (sc.atLineStart) {
1048 sc.SetState(SCE_C_DEFAULT|activitySet);
1050 break;
1051 case SCE_C_VERBATIM:
1052 if (options.verbatimStringsAllowEscapes && (sc.ch == '\\')) {
1053 sc.Forward(); // Skip all characters after the backslash
1054 } else if (sc.ch == '\"') {
1055 if (sc.chNext == '\"') {
1056 sc.Forward();
1057 } else {
1058 sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
1061 break;
1062 case SCE_C_TRIPLEVERBATIM:
1063 if (sc.Match("\"\"\"")) {
1064 while (sc.Match('"')) {
1065 sc.Forward();
1067 sc.SetState(SCE_C_DEFAULT|activitySet);
1069 break;
1070 case SCE_C_UUID:
1071 if (sc.atLineEnd || sc.ch == ')') {
1072 sc.SetState(SCE_C_DEFAULT|activitySet);
1074 break;
1075 case SCE_C_TASKMARKER:
1076 if (isoperator(sc.ch) || IsASpace(sc.ch)) {
1077 sc.SetState(styleBeforeTaskMarker|activitySet);
1078 styleBeforeTaskMarker = SCE_C_DEFAULT;
1082 if (sc.atLineEnd && !atLineEndBeforeSwitch) {
1083 // State exit processing consumed characters up to end of line.
1084 lineCurrent++;
1085 lineEndNext = styler.LineEnd(lineCurrent);
1086 vlls.Add(lineCurrent, preproc);
1089 // Determine if a new state should be entered.
1090 if (MaskActive(sc.state) == SCE_C_DEFAULT) {
1091 if (sc.Match('@', '\"')) {
1092 sc.SetState(SCE_C_VERBATIM|activitySet);
1093 sc.Forward();
1094 } else if (options.triplequotedStrings && sc.Match("\"\"\"")) {
1095 sc.SetState(SCE_C_TRIPLEVERBATIM|activitySet);
1096 sc.Forward(2);
1097 } else if (options.hashquotedStrings && sc.Match('#', '\"')) {
1098 sc.SetState(SCE_C_HASHQUOTEDSTRING|activitySet);
1099 sc.Forward();
1100 } else if (options.backQuotedStrings && sc.Match('`')) {
1101 sc.SetState(SCE_C_STRINGRAW|activitySet);
1102 rawStringTerminator = "`";
1103 } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
1104 if (lastWordWasUUID) {
1105 sc.SetState(SCE_C_UUID|activitySet);
1106 lastWordWasUUID = false;
1107 } else {
1108 sc.SetState(SCE_C_NUMBER|activitySet);
1110 } else if (!sc.atLineEnd && (setWordStart.Contains(sc.ch) || (sc.ch == '@'))) {
1111 if (lastWordWasUUID) {
1112 sc.SetState(SCE_C_UUID|activitySet);
1113 lastWordWasUUID = false;
1114 } else {
1115 sc.SetState(SCE_C_IDENTIFIER|activitySet);
1117 } else if (sc.Match('/', '*')) {
1118 if (sc.Match("/**") || sc.Match("/*!")) { // Support of Qt/Doxygen doc. style
1119 sc.SetState(SCE_C_COMMENTDOC|activitySet);
1120 } else {
1121 sc.SetState(SCE_C_COMMENT|activitySet);
1123 sc.Forward(); // Eat the * so it isn't used for the end of the comment
1124 } else if (sc.Match('/', '/')) {
1125 if ((sc.Match("///") && !sc.Match("////")) || sc.Match("//!"))
1126 // Support of Qt/Doxygen doc. style
1127 sc.SetState(SCE_C_COMMENTLINEDOC|activitySet);
1128 else
1129 sc.SetState(SCE_C_COMMENTLINE|activitySet);
1130 } else if (sc.ch == '/'
1131 && (setOKBeforeRE.Contains(chPrevNonWhite)
1132 || followsReturnKeyword(sc, styler))
1133 && (!setCouldBePostOp.Contains(chPrevNonWhite)
1134 || !FollowsPostfixOperator(sc, styler))) {
1135 sc.SetState(SCE_C_REGEX|activitySet); // JavaScript's RegEx
1136 inRERange = false;
1137 } else if (sc.ch == '\"') {
1138 if (sc.chPrev == 'R') {
1139 styler.Flush();
1140 if (MaskActive(styler.StyleAt(sc.currentPos - 1)) == SCE_C_STRINGRAW) {
1141 sc.SetState(SCE_C_STRINGRAW|activitySet);
1142 rawStringTerminator = ")";
1143 for (Sci_Position termPos = sc.currentPos + 1;; termPos++) {
1144 char chTerminator = styler.SafeGetCharAt(termPos, '(');
1145 if (chTerminator == '(')
1146 break;
1147 rawStringTerminator += chTerminator;
1149 rawStringTerminator += '\"';
1150 } else {
1151 sc.SetState(SCE_C_STRING|activitySet);
1153 } else {
1154 sc.SetState(SCE_C_STRING|activitySet);
1156 isIncludePreprocessor = false; // ensure that '>' won't end the string
1157 } else if (isIncludePreprocessor && sc.ch == '<') {
1158 sc.SetState(SCE_C_STRING|activitySet);
1159 } else if (sc.ch == '\'') {
1160 sc.SetState(SCE_C_CHARACTER|activitySet);
1161 } else if (sc.ch == '#' && visibleChars == 0) {
1162 // Preprocessor commands are alone on their line
1163 sc.SetState(SCE_C_PREPROCESSOR|activitySet);
1164 // Skip whitespace between # and preprocessor word
1165 do {
1166 sc.Forward();
1167 } while ((sc.ch == ' ' || sc.ch == '\t') && sc.More());
1168 if (sc.atLineEnd) {
1169 sc.SetState(SCE_C_DEFAULT|activitySet);
1170 } else if (sc.Match("include")) {
1171 isIncludePreprocessor = true;
1172 } else {
1173 if (options.trackPreprocessor) {
1174 if (sc.Match("ifdef") || sc.Match("ifndef")) {
1175 bool isIfDef = sc.Match("ifdef");
1176 int i = isIfDef ? 5 : 6;
1177 std::string restOfLine = GetRestOfLine(styler, sc.currentPos + i + 1, false);
1178 bool foundDef = preprocessorDefinitions.find(restOfLine) != preprocessorDefinitions.end();
1179 preproc.StartSection(isIfDef == foundDef);
1180 } else if (sc.Match("if")) {
1181 std::string restOfLine = GetRestOfLine(styler, sc.currentPos + 2, true);
1182 bool ifGood = EvaluateExpression(restOfLine, preprocessorDefinitions);
1183 preproc.StartSection(ifGood);
1184 } else if (sc.Match("else")) {
1185 if (!preproc.CurrentIfTaken()) {
1186 preproc.InvertCurrentLevel();
1187 activitySet = preproc.IsInactive() ? activeFlag : 0;
1188 if (!activitySet)
1189 sc.ChangeState(SCE_C_PREPROCESSOR|activitySet);
1190 } else if (!preproc.IsInactive()) {
1191 preproc.InvertCurrentLevel();
1192 activitySet = preproc.IsInactive() ? activeFlag : 0;
1193 if (!activitySet)
1194 sc.ChangeState(SCE_C_PREPROCESSOR|activitySet);
1196 } else if (sc.Match("elif")) {
1197 // Ensure only one chosen out of #if .. #elif .. #elif .. #else .. #endif
1198 if (!preproc.CurrentIfTaken()) {
1199 // Similar to #if
1200 std::string restOfLine = GetRestOfLine(styler, sc.currentPos + 2, true);
1201 bool ifGood = EvaluateExpression(restOfLine, preprocessorDefinitions);
1202 if (ifGood) {
1203 preproc.InvertCurrentLevel();
1204 activitySet = preproc.IsInactive() ? activeFlag : 0;
1205 if (!activitySet)
1206 sc.ChangeState(SCE_C_PREPROCESSOR|activitySet);
1208 } else if (!preproc.IsInactive()) {
1209 preproc.InvertCurrentLevel();
1210 activitySet = preproc.IsInactive() ? activeFlag : 0;
1211 if (!activitySet)
1212 sc.ChangeState(SCE_C_PREPROCESSOR|activitySet);
1214 } else if (sc.Match("endif")) {
1215 preproc.EndSection();
1216 activitySet = preproc.IsInactive() ? activeFlag : 0;
1217 sc.ChangeState(SCE_C_PREPROCESSOR|activitySet);
1218 } else if (sc.Match("define")) {
1219 if (options.updatePreprocessor && !preproc.IsInactive()) {
1220 std::string restOfLine = GetRestOfLine(styler, sc.currentPos + 6, true);
1221 size_t startName = 0;
1222 while ((startName < restOfLine.length()) && IsSpaceOrTab(restOfLine[startName]))
1223 startName++;
1224 size_t endName = startName;
1225 while ((endName < restOfLine.length()) && setWord.Contains(static_cast<unsigned char>(restOfLine[endName])))
1226 endName++;
1227 std::string key = restOfLine.substr(startName, endName-startName);
1228 if ((endName < restOfLine.length()) && (restOfLine.at(endName) == '(')) {
1229 // Macro
1230 size_t endArgs = endName;
1231 while ((endArgs < restOfLine.length()) && (restOfLine[endArgs] != ')'))
1232 endArgs++;
1233 std::string args = restOfLine.substr(endName + 1, endArgs - endName - 1);
1234 size_t startValue = endArgs+1;
1235 while ((startValue < restOfLine.length()) && IsSpaceOrTab(restOfLine[startValue]))
1236 startValue++;
1237 std::string value;
1238 if (startValue < restOfLine.length())
1239 value = restOfLine.substr(startValue);
1240 preprocessorDefinitions[key] = SymbolValue(value, args);
1241 ppDefineHistory.push_back(PPDefinition(lineCurrent, key, value, false, args));
1242 definitionsChanged = true;
1243 } else {
1244 // Value
1245 size_t startValue = endName;
1246 while ((startValue < restOfLine.length()) && IsSpaceOrTab(restOfLine[startValue]))
1247 startValue++;
1248 std::string value = restOfLine.substr(startValue);
1249 preprocessorDefinitions[key] = value;
1250 ppDefineHistory.push_back(PPDefinition(lineCurrent, key, value));
1251 definitionsChanged = true;
1254 } else if (sc.Match("undef")) {
1255 if (options.updatePreprocessor && !preproc.IsInactive()) {
1256 const std::string restOfLine = GetRestOfLine(styler, sc.currentPos + 5, false);
1257 std::vector<std::string> tokens = Tokenize(restOfLine);
1258 if (tokens.size() >= 1) {
1259 const std::string key = tokens[0];
1260 preprocessorDefinitions.erase(key);
1261 ppDefineHistory.push_back(PPDefinition(lineCurrent, key, "", true));
1262 definitionsChanged = true;
1268 } else if (isoperator(sc.ch)) {
1269 sc.SetState(SCE_C_OPERATOR|activitySet);
1273 if (!IsASpace(sc.ch) && !IsSpaceEquiv(MaskActive(sc.state))) {
1274 chPrevNonWhite = sc.ch;
1275 visibleChars++;
1277 continuationLine = false;
1278 sc.Forward();
1280 const bool rawStringsChanged = rawStringTerminators.Merge(rawSTNew, lineCurrent);
1281 if (definitionsChanged || rawStringsChanged)
1282 styler.ChangeLexerState(startPos, startPos + length);
1283 sc.Complete();
1286 // Store both the current line's fold level and the next lines in the
1287 // level store to make it easy to pick up with each increment
1288 // and to make it possible to fiddle the current level for "} else {".
1290 void SCI_METHOD LexerCPP::Fold(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) {
1292 if (!options.fold)
1293 return;
1295 LexAccessor styler(pAccess);
1297 Sci_PositionU endPos = startPos + length;
1298 int visibleChars = 0;
1299 bool inLineComment = false;
1300 Sci_Position lineCurrent = styler.GetLine(startPos);
1301 int levelCurrent = SC_FOLDLEVELBASE;
1302 if (lineCurrent > 0)
1303 levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
1304 Sci_PositionU lineStartNext = styler.LineStart(lineCurrent+1);
1305 int levelMinCurrent = levelCurrent;
1306 int levelNext = levelCurrent;
1307 char chNext = styler[startPos];
1308 int styleNext = MaskActive(styler.StyleAt(startPos));
1309 int style = MaskActive(initStyle);
1310 const bool userDefinedFoldMarkers = !options.foldExplicitStart.empty() && !options.foldExplicitEnd.empty();
1311 for (Sci_PositionU i = startPos; i < endPos; i++) {
1312 char ch = chNext;
1313 chNext = styler.SafeGetCharAt(i + 1);
1314 int stylePrev = style;
1315 style = styleNext;
1316 styleNext = MaskActive(styler.StyleAt(i + 1));
1317 bool atEOL = i == (lineStartNext-1);
1318 if ((style == SCE_C_COMMENTLINE) || (style == SCE_C_COMMENTLINEDOC))
1319 inLineComment = true;
1320 if (options.foldComment && options.foldCommentMultiline && IsStreamCommentStyle(style) && !inLineComment) {
1321 if (!IsStreamCommentStyle(stylePrev)) {
1322 levelNext++;
1323 } else if (!IsStreamCommentStyle(styleNext) && !atEOL) {
1324 // Comments don't end at end of line and the next character may be unstyled.
1325 levelNext--;
1328 if (options.foldComment && options.foldCommentExplicit && ((style == SCE_C_COMMENTLINE) || options.foldExplicitAnywhere)) {
1329 if (userDefinedFoldMarkers) {
1330 if (styler.Match(i, options.foldExplicitStart.c_str())) {
1331 levelNext++;
1332 } else if (styler.Match(i, options.foldExplicitEnd.c_str())) {
1333 levelNext--;
1335 } else {
1336 if ((ch == '/') && (chNext == '/')) {
1337 char chNext2 = styler.SafeGetCharAt(i + 2);
1338 if (chNext2 == '{') {
1339 levelNext++;
1340 } else if (chNext2 == '}') {
1341 levelNext--;
1346 if (options.foldPreprocessor && (style == SCE_C_PREPROCESSOR)) {
1347 if (ch == '#') {
1348 Sci_PositionU j = i + 1;
1349 while ((j < endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) {
1350 j++;
1352 if (styler.Match(j, "region") || styler.Match(j, "if")) {
1353 levelNext++;
1354 } else if (styler.Match(j, "end")) {
1355 levelNext--;
1358 if (options.foldPreprocessorAtElse && (styler.Match(j, "else") || styler.Match(j, "elif"))) {
1359 levelMinCurrent--;
1363 if (options.foldSyntaxBased && (style == SCE_C_OPERATOR)) {
1364 if (ch == '{' || ch == '[' || ch == '(') {
1365 // Measure the minimum before a '{' to allow
1366 // folding on "} else {"
1367 if (options.foldAtElse && levelMinCurrent > levelNext) {
1368 levelMinCurrent = levelNext;
1370 levelNext++;
1371 } else if (ch == '}' || ch == ']' || ch == ')') {
1372 levelNext--;
1375 if (!IsASpace(ch))
1376 visibleChars++;
1377 if (atEOL || (i == endPos-1)) {
1378 int levelUse = levelCurrent;
1379 if ((options.foldSyntaxBased && options.foldAtElse) ||
1380 (options.foldPreprocessor && options.foldPreprocessorAtElse)
1382 levelUse = levelMinCurrent;
1384 int lev = levelUse | levelNext << 16;
1385 if (visibleChars == 0 && options.foldCompact)
1386 lev |= SC_FOLDLEVELWHITEFLAG;
1387 if (levelUse < levelNext)
1388 lev |= SC_FOLDLEVELHEADERFLAG;
1389 if (lev != styler.LevelAt(lineCurrent)) {
1390 styler.SetLevel(lineCurrent, lev);
1392 lineCurrent++;
1393 lineStartNext = styler.LineStart(lineCurrent+1);
1394 levelCurrent = levelNext;
1395 levelMinCurrent = levelCurrent;
1396 if (atEOL && (i == static_cast<Sci_PositionU>(styler.Length()-1))) {
1397 // There is an empty line at end of file so give it same level and empty
1398 styler.SetLevel(lineCurrent, (levelCurrent | levelCurrent << 16) | SC_FOLDLEVELWHITEFLAG);
1400 visibleChars = 0;
1401 inLineComment = false;
1406 void LexerCPP::EvaluateTokens(std::vector<std::string> &tokens, const SymbolTable &preprocessorDefinitions) {
1408 // Remove whitespace tokens
1409 tokens.erase(std::remove_if(tokens.begin(), tokens.end(), OnlySpaceOrTab), tokens.end());
1411 // Evaluate defined statements to either 0 or 1
1412 for (size_t i=0; (i+1)<tokens.size();) {
1413 if (tokens[i] == "defined") {
1414 const char *val = "0";
1415 if (tokens[i+1] == "(") {
1416 if (((i + 2)<tokens.size()) && (tokens[i + 2] == ")")) {
1417 // defined()
1418 tokens.erase(tokens.begin() + i + 1, tokens.begin() + i + 3);
1419 } else if (((i+3)<tokens.size()) && (tokens[i+3] == ")")) {
1420 // defined(<identifier>)
1421 SymbolTable::const_iterator it = preprocessorDefinitions.find(tokens[i+2]);
1422 if (it != preprocessorDefinitions.end()) {
1423 val = "1";
1425 tokens.erase(tokens.begin() + i + 1, tokens.begin() + i + 4);
1426 } else {
1427 // Spurious '(' so erase as more likely to result in false
1428 tokens.erase(tokens.begin() + i + 1, tokens.begin() + i + 2);
1430 } else {
1431 // defined <identifier>
1432 SymbolTable::const_iterator it = preprocessorDefinitions.find(tokens[i+1]);
1433 if (it != preprocessorDefinitions.end()) {
1434 val = "1";
1437 tokens[i] = val;
1438 } else {
1439 i++;
1443 // Evaluate identifiers
1444 const size_t maxIterations = 100;
1445 size_t iterations = 0; // Limit number of iterations in case there is a recursive macro.
1446 for (size_t i = 0; (i<tokens.size()) && (iterations < maxIterations);) {
1447 iterations++;
1448 if (setWordStart.Contains(static_cast<unsigned char>(tokens[i][0]))) {
1449 SymbolTable::const_iterator it = preprocessorDefinitions.find(tokens[i]);
1450 if (it != preprocessorDefinitions.end()) {
1451 // Tokenize value
1452 std::vector<std::string> macroTokens = Tokenize(it->second.value);
1453 if (it->second.IsMacro()) {
1454 if ((i + 1 < tokens.size()) && (tokens.at(i + 1) == "(")) {
1455 // Create map of argument name to value
1456 std::vector<std::string> argumentNames = StringSplit(it->second.arguments, ',');
1457 std::map<std::string, std::string> arguments;
1458 size_t arg = 0;
1459 size_t tok = i+2;
1460 while ((tok < tokens.size()) && (arg < argumentNames.size()) && (tokens.at(tok) != ")")) {
1461 if (tokens.at(tok) != ",") {
1462 arguments[argumentNames.at(arg)] = tokens.at(tok);
1463 arg++;
1465 tok++;
1468 // Remove invocation
1469 tokens.erase(tokens.begin() + i, tokens.begin() + tok + 1);
1471 // Substitute values into macro
1472 macroTokens.erase(std::remove_if(macroTokens.begin(), macroTokens.end(), OnlySpaceOrTab), macroTokens.end());
1474 for (size_t iMacro = 0; iMacro < macroTokens.size();) {
1475 if (setWordStart.Contains(static_cast<unsigned char>(macroTokens[iMacro][0]))) {
1476 std::map<std::string, std::string>::const_iterator itFind = arguments.find(macroTokens[iMacro]);
1477 if (itFind != arguments.end()) {
1478 // TODO: Possible that value will be expression so should insert tokenized form
1479 macroTokens[iMacro] = itFind->second;
1482 iMacro++;
1485 // Insert results back into tokens
1486 tokens.insert(tokens.begin() + i, macroTokens.begin(), macroTokens.end());
1488 } else {
1489 i++;
1491 } else {
1492 // Remove invocation
1493 tokens.erase(tokens.begin() + i);
1494 // Insert results back into tokens
1495 tokens.insert(tokens.begin() + i, macroTokens.begin(), macroTokens.end());
1497 } else {
1498 // Identifier not found
1499 tokens.erase(tokens.begin() + i);
1501 } else {
1502 i++;
1506 // Find bracketed subexpressions and recurse on them
1507 BracketPair bracketPair = FindBracketPair(tokens);
1508 while (bracketPair.itBracket != tokens.end()) {
1509 std::vector<std::string> inBracket(bracketPair.itBracket + 1, bracketPair.itEndBracket);
1510 EvaluateTokens(inBracket, preprocessorDefinitions);
1512 // The insertion is done before the removal because there were failures with the opposite approach
1513 tokens.insert(bracketPair.itBracket, inBracket.begin(), inBracket.end());
1515 bracketPair = FindBracketPair(tokens);
1516 tokens.erase(bracketPair.itBracket, bracketPair.itEndBracket + 1);
1518 bracketPair = FindBracketPair(tokens);
1521 // Evaluate logical negations
1522 for (size_t j=0; (j+1)<tokens.size();) {
1523 if (setNegationOp.Contains(tokens[j][0])) {
1524 int isTrue = atoi(tokens[j+1].c_str());
1525 if (tokens[j] == "!")
1526 isTrue = !isTrue;
1527 std::vector<std::string>::iterator itInsert =
1528 tokens.erase(tokens.begin() + j, tokens.begin() + j + 2);
1529 tokens.insert(itInsert, isTrue ? "1" : "0");
1530 } else {
1531 j++;
1535 // Evaluate expressions in precedence order
1536 enum precedence { precArithmetic, precRelative, precLogical };
1537 for (int prec=precArithmetic; prec <= precLogical; prec++) {
1538 // Looking at 3 tokens at a time so end at 2 before end
1539 for (size_t k=0; (k+2)<tokens.size();) {
1540 char chOp = tokens[k+1][0];
1541 if (
1542 ((prec==precArithmetic) && setArithmethicOp.Contains(chOp)) ||
1543 ((prec==precRelative) && setRelOp.Contains(chOp)) ||
1544 ((prec==precLogical) && setLogicalOp.Contains(chOp))
1546 int valA = atoi(tokens[k].c_str());
1547 int valB = atoi(tokens[k+2].c_str());
1548 int result = 0;
1549 if (tokens[k+1] == "+")
1550 result = valA + valB;
1551 else if (tokens[k+1] == "-")
1552 result = valA - valB;
1553 else if (tokens[k+1] == "*")
1554 result = valA * valB;
1555 else if (tokens[k+1] == "/")
1556 result = valA / (valB ? valB : 1);
1557 else if (tokens[k+1] == "%")
1558 result = valA % (valB ? valB : 1);
1559 else if (tokens[k+1] == "<")
1560 result = valA < valB;
1561 else if (tokens[k+1] == "<=")
1562 result = valA <= valB;
1563 else if (tokens[k+1] == ">")
1564 result = valA > valB;
1565 else if (tokens[k+1] == ">=")
1566 result = valA >= valB;
1567 else if (tokens[k+1] == "==")
1568 result = valA == valB;
1569 else if (tokens[k+1] == "!=")
1570 result = valA != valB;
1571 else if (tokens[k+1] == "||")
1572 result = valA || valB;
1573 else if (tokens[k+1] == "&&")
1574 result = valA && valB;
1575 char sResult[30];
1576 sprintf(sResult, "%d", result);
1577 std::vector<std::string>::iterator itInsert =
1578 tokens.erase(tokens.begin() + k, tokens.begin() + k + 3);
1579 tokens.insert(itInsert, sResult);
1580 } else {
1581 k++;
1587 std::vector<std::string> LexerCPP::Tokenize(const std::string &expr) const {
1588 // Break into tokens
1589 std::vector<std::string> tokens;
1590 const char *cp = expr.c_str();
1591 while (*cp) {
1592 std::string word;
1593 if (setWord.Contains(static_cast<unsigned char>(*cp))) {
1594 // Identifiers and numbers
1595 while (setWord.Contains(static_cast<unsigned char>(*cp))) {
1596 word += *cp;
1597 cp++;
1599 } else if (IsSpaceOrTab(*cp)) {
1600 while (IsSpaceOrTab(*cp)) {
1601 word += *cp;
1602 cp++;
1604 } else if (setRelOp.Contains(static_cast<unsigned char>(*cp))) {
1605 word += *cp;
1606 cp++;
1607 if (setRelOp.Contains(static_cast<unsigned char>(*cp))) {
1608 word += *cp;
1609 cp++;
1611 } else if (setLogicalOp.Contains(static_cast<unsigned char>(*cp))) {
1612 word += *cp;
1613 cp++;
1614 if (setLogicalOp.Contains(static_cast<unsigned char>(*cp))) {
1615 word += *cp;
1616 cp++;
1618 } else {
1619 // Should handle strings, characters, and comments here
1620 word += *cp;
1621 cp++;
1623 tokens.push_back(word);
1625 return tokens;
1628 bool LexerCPP::EvaluateExpression(const std::string &expr, const SymbolTable &preprocessorDefinitions) {
1629 std::vector<std::string> tokens = Tokenize(expr);
1631 EvaluateTokens(tokens, preprocessorDefinitions);
1633 // "0" or "" -> false else true
1634 bool isFalse = tokens.empty() ||
1635 ((tokens.size() == 1) && ((tokens[0] == "") || tokens[0] == "0"));
1636 return !isFalse;
1639 LexerModule lmCPP(SCLEX_CPP, LexerCPP::LexerFactoryCPP, "cpp", cppWordLists);
1640 LexerModule lmCPPNoCase(SCLEX_CPPNOCASE, LexerCPP::LexerFactoryCPPInsensitive, "cppnocase", cppWordLists);