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.
8 
9 #include <stdlib.h>
10 #include <string.h>
11 #include <ctype.h>
12 #include <stdio.h>
13 #include <stdarg.h>
14 #include <assert.h>
15 
16 #ifdef _MSC_VER
17 #pragma warning(disable: 4786)
18 #endif
19 
20 #include <string>
21 #include <vector>
22 #include <map>
23 #include <algorithm>
24 
25 #include "ILexer.h"
26 #include "Scintilla.h"
27 #include "SciLexer.h"
28 
29 #include "WordList.h"
30 #include "LexAccessor.h"
31 #include "Accessor.h"
32 #include "StyleContext.h"
33 #include "CharacterSet.h"
34 #include "LexerModule.h"
35 #include "OptionSet.h"
36 #include "SparseState.h"
37 
38 #ifdef SCI_NAMESPACE
39 using namespace Scintilla;
40 #endif
41 
IsSpaceEquiv(int state)42 static 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);
47 }
48 
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.
FollowsPostfixOperator(StyleContext & sc,LexAccessor & styler)56 static bool FollowsPostfixOperator(StyleContext &sc, LexAccessor &styler) {
57 	int pos = (int) sc.currentPos;
58 	while (--pos > 0) {
59 		char ch = styler[pos];
60 		if (ch == '+' || ch == '-') {
61 			return styler[pos - 1] == ch;
62 		}
63 	}
64 	return false;
65 }
66 
followsReturnKeyword(StyleContext & sc,LexAccessor & styler)67 static bool followsReturnKeyword(StyleContext &sc, LexAccessor &styler) {
68 	// Don't look at styles, so no need to flush.
69 	int pos = (int) sc.currentPos;
70 	int currentLine = styler.GetLine(pos);
71 	int lineStartPos = styler.LineStart(currentLine);
72 	char ch;
73 	while (--pos > lineStartPos) {
74 		ch = styler.SafeGetCharAt(pos);
75 		if (ch != ' ' && ch != '\t') {
76 			break;
77 		}
78 	}
79 	const char *retBack = "nruter";
80 	const char *s = retBack;
81 	while (*s
82 		&& pos >= lineStartPos
83 		&& styler.SafeGetCharAt(pos) == *s) {
84 		s++;
85 		pos--;
86 	}
87 	return !*s;
88 }
89 
GetRestOfLine(LexAccessor & styler,int start,bool allowSpace)90 static std::string GetRestOfLine(LexAccessor &styler, int start, bool allowSpace) {
91 	std::string restOfLine;
92 	int i =0;
93 	char ch = styler.SafeGetCharAt(start + i, '\n');
94 	while ((ch != '\r') && (ch != '\n')) {
95 		if (allowSpace || (ch != ' '))
96 			restOfLine += ch;
97 		i++;
98 		ch = styler.SafeGetCharAt(start + i, '\n');
99 	}
100 	return restOfLine;
101 }
102 
IsStreamCommentStyle(int style)103 static bool IsStreamCommentStyle(int style) {
104 	return style == SCE_C_COMMENT ||
105 		style == SCE_C_COMMENTDOC ||
106 		style == SCE_C_COMMENTDOCKEYWORD ||
107 		style == SCE_C_COMMENTDOCKEYWORDERROR;
108 }
109 
Tokenize(const std::string & s)110 static std::vector<std::string> Tokenize(const std::string &s) {
111 	// Break into space separated tokens
112 	std::string word;
113 	std::vector<std::string> tokens;
114 	for (const char *cp = s.c_str(); *cp; cp++) {
115 		if ((*cp == ' ') || (*cp == '\t')) {
116 			if (!word.empty()) {
117 				tokens.push_back(word);
118 				word = "";
119 			}
120 		} else {
121 			word += *cp;
122 		}
123 	}
124 	if (!word.empty()) {
125 		tokens.push_back(word);
126 	}
127 	return tokens;
128 }
129 
130 struct PPDefinition {
131 	int line;
132 	std::string key;
133 	std::string value;
PPDefinitionPPDefinition134 	PPDefinition(int line_, const std::string &key_, const std::string &value_) :
135 		line(line_), key(key_), value(value_) {
136 	}
137 };
138 
139 class LinePPState {
140 	int state;
141 	int ifTaken;
142 	int level;
ValidLevel() const143 	bool ValidLevel() const {
144 		return level >= 0 && level < 32;
145 	}
maskLevel() const146 	int maskLevel() const {
147 		return 1 << level;
148 	}
149 public:
LinePPState()150 	LinePPState() : state(0), ifTaken(0), level(-1) {
151 	}
IsInactive() const152 	bool IsInactive() const {
153 		return state != 0;
154 	}
CurrentIfTaken()155 	bool CurrentIfTaken() {
156 		return (ifTaken & maskLevel()) != 0;
157 	}
StartSection(bool on)158 	void StartSection(bool on) {
159 		level++;
160 		if (ValidLevel()) {
161 			if (on) {
162 				state &= ~maskLevel();
163 				ifTaken |= maskLevel();
164 			} else {
165 				state |= maskLevel();
166 				ifTaken &= ~maskLevel();
167 			}
168 		}
169 	}
EndSection()170 	void EndSection() {
171 		if (ValidLevel()) {
172 			state &= ~maskLevel();
173 			ifTaken &= ~maskLevel();
174 		}
175 		level--;
176 	}
InvertCurrentLevel()177 	void InvertCurrentLevel() {
178 		if (ValidLevel()) {
179 			state ^= maskLevel();
180 			ifTaken |= maskLevel();
181 		}
182 	}
183 };
184 
185 // Hold the preprocessor state for each line seen.
186 // Currently one entry per line but could become sparse with just one entry per preprocessor line.
187 class PPStates {
188 	std::vector<LinePPState> vlls;
189 public:
ForLine(int line)190 	LinePPState ForLine(int line) {
191 		if ((line > 0) && (vlls.size() > static_cast<size_t>(line))) {
192 			return vlls[line];
193 		} else {
194 			return LinePPState();
195 		}
196 	}
Add(int line,LinePPState lls)197 	void Add(int line, LinePPState lls) {
198 		vlls.resize(line+1);
199 		vlls[line] = lls;
200 	}
201 };
202 
203 // An individual named option for use in an OptionSet
204 
205 // Options used for LexerCPP
206 struct OptionsCPP {
207 	bool stylingWithinPreprocessor;
208 	bool identifiersAllowDollars;
209 	bool trackPreprocessor;
210 	bool updatePreprocessor;
211 	bool triplequotedStrings;
212 	bool fold;
213 	bool foldSyntaxBased;
214 	bool foldComment;
215 	bool foldCommentMultiline;
216 	bool foldCommentExplicit;
217 	std::string foldExplicitStart;
218 	std::string foldExplicitEnd;
219 	bool foldExplicitAnywhere;
220 	bool foldPreprocessor;
221 	bool foldCompact;
222 	bool foldAtElse;
OptionsCPPOptionsCPP223 	OptionsCPP() {
224 		stylingWithinPreprocessor = false;
225 		identifiersAllowDollars = true;
226 		trackPreprocessor = true;
227 		updatePreprocessor = true;
228 		triplequotedStrings = false;
229 		fold = false;
230 		foldSyntaxBased = true;
231 		foldComment = false;
232 		foldCommentMultiline = true;
233 		foldCommentExplicit = true;
234 		foldExplicitStart = "";
235 		foldExplicitEnd = "";
236 		foldExplicitAnywhere = false;
237 		foldPreprocessor = false;
238 		foldCompact = false;
239 		foldAtElse = false;
240 	}
241 };
242 
243 static const char *const cppWordLists[] = {
244             "Primary keywords and identifiers",
245             "Secondary keywords and identifiers",
246             "Documentation comment keywords",
247             "Global classes and typedefs",
248             "Preprocessor definitions",
249             0,
250 };
251 
252 struct OptionSetCPP : public OptionSet<OptionsCPP> {
OptionSetCPPOptionSetCPP253 	OptionSetCPP() {
254 		DefineProperty("styling.within.preprocessor", &OptionsCPP::stylingWithinPreprocessor,
255 			"For C++ code, determines whether all preprocessor code is styled in the "
256 			"preprocessor style (0, the default) or only from the initial # to the end "
257 			"of the command word(1).");
258 
259 		DefineProperty("lexer.cpp.allow.dollars", &OptionsCPP::identifiersAllowDollars,
260 			"Set to 0 to disallow the '$' character in identifiers with the cpp lexer.");
261 
262 		DefineProperty("lexer.cpp.track.preprocessor", &OptionsCPP::trackPreprocessor,
263 			"Set to 1 to interpret #if/#else/#endif to grey out code that is not active.");
264 
265 		DefineProperty("lexer.cpp.update.preprocessor", &OptionsCPP::updatePreprocessor,
266 			"Set to 1 to update preprocessor definitions when #define found.");
267 
268 		DefineProperty("lexer.cpp.triplequoted.strings", &OptionsCPP::triplequotedStrings,
269 			"Set to 1 to enable highlighting of triple-quoted strings.");
270 
271 		DefineProperty("fold", &OptionsCPP::fold);
272 
273 		DefineProperty("fold.cpp.syntax.based", &OptionsCPP::foldSyntaxBased,
274 			"Set this property to 0 to disable syntax based folding.");
275 
276 		DefineProperty("fold.comment", &OptionsCPP::foldComment,
277 			"This option enables folding multi-line comments and explicit fold points when using the C++ lexer. "
278 			"Explicit fold points allows adding extra folding by placing a //{ comment at the start and a //} "
279 			"at the end of a section that should fold.");
280 
281 		DefineProperty("fold.cpp.comment.multiline", &OptionsCPP::foldCommentMultiline,
282 			"Set this property to 0 to disable folding multi-line comments when fold.comment=1.");
283 
284 		DefineProperty("fold.cpp.comment.explicit", &OptionsCPP::foldCommentExplicit,
285 			"Set this property to 0 to disable folding explicit fold points when fold.comment=1.");
286 
287 		DefineProperty("fold.cpp.explicit.start", &OptionsCPP::foldExplicitStart,
288 			"The string to use for explicit fold start points, replacing the standard //{.");
289 
290 		DefineProperty("fold.cpp.explicit.end", &OptionsCPP::foldExplicitEnd,
291 			"The string to use for explicit fold end points, replacing the standard //}.");
292 
293 		DefineProperty("fold.cpp.explicit.anywhere", &OptionsCPP::foldExplicitAnywhere,
294 			"Set this property to 1 to enable explicit fold points anywhere, not just in line comments.");
295 
296 		DefineProperty("fold.preprocessor", &OptionsCPP::foldPreprocessor,
297 			"This option enables folding preprocessor directives when using the C++ lexer. "
298 			"Includes C#'s explicit #region and #endregion folding directives.");
299 
300 		DefineProperty("fold.compact", &OptionsCPP::foldCompact);
301 
302 		DefineProperty("fold.at.else", &OptionsCPP::foldAtElse,
303 			"This option enables C++ folding on a \"} else {\" line of an if statement.");
304 
305 		DefineWordListSets(cppWordLists);
306 	}
307 };
308 
309 class LexerCPP : public ILexer {
310 	bool caseSensitive;
311 	CharacterSet setWord;
312 	CharacterSet setNegationOp;
313 	CharacterSet setArithmethicOp;
314 	CharacterSet setRelOp;
315 	CharacterSet setLogicalOp;
316 	PPStates vlls;
317 	std::vector<PPDefinition> ppDefineHistory;
318 	WordList keywords;
319 	WordList keywords2;
320 	WordList keywords3;
321 	WordList keywords4;
322 	WordList ppDefinitions;
323 	std::map<std::string, std::string> preprocessorDefinitionsStart;
324 	OptionsCPP options;
325 	OptionSetCPP osCPP;
326 	SparseState<std::string> rawStringTerminators;
327 	enum { activeFlag = 0x40 };
328 public:
LexerCPP(bool caseSensitive_)329 	LexerCPP(bool caseSensitive_) :
330 		caseSensitive(caseSensitive_),
331 		setWord(CharacterSet::setAlphaNum, "._", 0x80, true),
332 		setNegationOp(CharacterSet::setNone, "!"),
333 		setArithmethicOp(CharacterSet::setNone, "+-/*%"),
334 		setRelOp(CharacterSet::setNone, "=!<>"),
335 		setLogicalOp(CharacterSet::setNone, "|&") {
336 	}
~LexerCPP()337 	~LexerCPP() {
338 	}
Release()339 	void SCI_METHOD Release() {
340 		delete this;
341 	}
Version() const342 	int SCI_METHOD Version() const {
343 		return lvOriginal;
344 	}
PropertyNames()345 	const char * SCI_METHOD PropertyNames() {
346 		return osCPP.PropertyNames();
347 	}
PropertyType(const char * name)348 	int SCI_METHOD PropertyType(const char *name) {
349 		return osCPP.PropertyType(name);
350 	}
DescribeProperty(const char * name)351 	const char * SCI_METHOD DescribeProperty(const char *name) {
352 		return osCPP.DescribeProperty(name);
353 	}
354 	int SCI_METHOD PropertySet(const char *key, const char *val);
DescribeWordListSets()355 	const char * SCI_METHOD DescribeWordListSets() {
356 		return osCPP.DescribeWordListSets();
357 	}
358 	int SCI_METHOD WordListSet(int n, const char *wl);
359 	void SCI_METHOD Lex(unsigned int startPos, int length, int initStyle, IDocument *pAccess);
360 	void SCI_METHOD Fold(unsigned int startPos, int length, int initStyle, IDocument *pAccess);
361 
PrivateCall(int,void *)362 	void * SCI_METHOD PrivateCall(int, void *) {
363 		return 0;
364 	}
365 
LexerFactoryCPP()366 	static ILexer *LexerFactoryCPP() {
367 		return new LexerCPP(true);
368 	}
LexerFactoryCPPInsensitive()369 	static ILexer *LexerFactoryCPPInsensitive() {
370 		return new LexerCPP(false);
371 	}
MaskActive(int style)372 	static int MaskActive(int style) {
373 		return style & ~activeFlag;
374 	}
375 	void EvaluateTokens(std::vector<std::string> &tokens);
376 	bool EvaluateExpression(const std::string &expr, const std::map<std::string, std::string> &preprocessorDefinitions);
377 };
378 
PropertySet(const char * key,const char * val)379 int SCI_METHOD LexerCPP::PropertySet(const char *key, const char *val) {
380 	if (osCPP.PropertySet(&options, key, val)) {
381 		return 0;
382 	}
383 	return -1;
384 }
385 
WordListSet(int n,const char * wl)386 int SCI_METHOD LexerCPP::WordListSet(int n, const char *wl) {
387 	WordList *wordListN = 0;
388 	switch (n) {
389 	case 0:
390 		wordListN = &keywords;
391 		break;
392 	case 1:
393 		wordListN = &keywords2;
394 		break;
395 	case 2:
396 		wordListN = &keywords3;
397 		break;
398 	case 3:
399 		wordListN = &keywords4;
400 		break;
401 	case 4:
402 		wordListN = &ppDefinitions;
403 		break;
404 	}
405 	int firstModification = -1;
406 	if (wordListN) {
407 		WordList wlNew;
408 		wlNew.Set(wl);
409 		if (*wordListN != wlNew) {
410 			wordListN->Set(wl);
411 			firstModification = 0;
412 			if (n == 4) {
413 				// Rebuild preprocessorDefinitions
414 				preprocessorDefinitionsStart.clear();
415 				for (int nDefinition = 0; nDefinition < ppDefinitions.len; nDefinition++) {
416 					char *cpDefinition = ppDefinitions.words[nDefinition];
417 					char *cpEquals = strchr(cpDefinition, '=');
418 					if (cpEquals) {
419 						std::string name(cpDefinition, cpEquals - cpDefinition);
420 						std::string val(cpEquals+1);
421 						preprocessorDefinitionsStart[name] = val;
422 					} else {
423 						std::string name(cpDefinition);
424 						std::string val("1");
425 						preprocessorDefinitionsStart[name] = val;
426 					}
427 				}
428 			}
429 		}
430 	}
431 	return firstModification;
432 }
433 
434 // Functor used to truncate history
435 struct After {
436 	int line;
AfterAfter437 	After(int line_) : line(line_) {}
operator ()After438 	bool operator()(PPDefinition &p) const {
439 		return p.line > line;
440 	}
441 };
442 
Lex(unsigned int startPos,int length,int initStyle,IDocument * pAccess)443 void SCI_METHOD LexerCPP::Lex(unsigned int startPos, int length, int initStyle, IDocument *pAccess) {
444 	LexAccessor styler(pAccess);
445 
446 	CharacterSet setOKBeforeRE(CharacterSet::setNone, "([{=,:;!%^&*|?~+-");
447 	CharacterSet setCouldBePostOp(CharacterSet::setNone, "+-");
448 
449 	CharacterSet setDoxygen(CharacterSet::setAlpha, "$@\\&<>#{}[]");
450 
451 	CharacterSet setWordStart(CharacterSet::setAlpha, "_", 0x80, true);
452 
453 	if (options.identifiersAllowDollars) {
454 		setWordStart.Add('$');
455 		setWord.Add('$');
456 	}
457 
458 	int chPrevNonWhite = ' ';
459 	int visibleChars = 0;
460 	bool lastWordWasUUID = false;
461 	int styleBeforeDCKeyword = SCE_C_DEFAULT;
462 	bool continuationLine = false;
463 	bool isIncludePreprocessor = false;
464 
465 	int lineCurrent = styler.GetLine(startPos);
466 	if ((initStyle == SCE_C_PREPROCESSOR) ||
467       (initStyle == SCE_C_COMMENTLINE) ||
468       (initStyle == SCE_C_COMMENTLINEDOC)) {
469 		// Set continuationLine if last character of previous line is '\'
470 		if (lineCurrent > 0) {
471 			int chBack = styler.SafeGetCharAt(startPos-1, 0);
472 			int chBack2 = styler.SafeGetCharAt(startPos-2, 0);
473 			int lineEndChar = '!';
474 			if (chBack2 == '\r' && chBack == '\n') {
475 				lineEndChar = styler.SafeGetCharAt(startPos-3, 0);
476 			} else if (chBack == '\n' || chBack == '\r') {
477 				lineEndChar = chBack2;
478 			}
479 			continuationLine = lineEndChar == '\\';
480 		}
481 	}
482 
483 	// look back to set chPrevNonWhite properly for better regex colouring
484 	if (startPos > 0) {
485 		int back = startPos;
486 		while (--back && IsSpaceEquiv(styler.StyleAt(back)))
487 			;
488 		if (styler.StyleAt(back) == SCE_C_OPERATOR) {
489 			chPrevNonWhite = styler.SafeGetCharAt(back);
490 		}
491 	}
492 
493 	StyleContext sc(startPos, length, initStyle, styler, 0x7f);
494 	LinePPState preproc = vlls.ForLine(lineCurrent);
495 
496 	bool definitionsChanged = false;
497 
498 	// Truncate ppDefineHistory before current line
499 
500 	if (!options.updatePreprocessor)
501 		ppDefineHistory.clear();
502 
503 	std::vector<PPDefinition>::iterator itInvalid = std::find_if(ppDefineHistory.begin(), ppDefineHistory.end(), After(lineCurrent-1));
504 	if (itInvalid != ppDefineHistory.end()) {
505 		ppDefineHistory.erase(itInvalid, ppDefineHistory.end());
506 		definitionsChanged = true;
507 	}
508 
509 	std::map<std::string, std::string> preprocessorDefinitions = preprocessorDefinitionsStart;
510 	for (std::vector<PPDefinition>::iterator itDef = ppDefineHistory.begin(); itDef != ppDefineHistory.end(); ++itDef) {
511 		preprocessorDefinitions[itDef->key] = itDef->value;
512 	}
513 
514 	std::string rawStringTerminator = rawStringTerminators.ValueAt(lineCurrent-1);
515 	SparseState<std::string> rawSTNew(lineCurrent);
516 
517 	int activitySet = preproc.IsInactive() ? activeFlag : 0;
518 
519 	for (; sc.More(); sc.Forward()) {
520 
521 		if (sc.atLineStart) {
522 			if ((sc.state == SCE_C_STRING) || (sc.state == SCE_C_CHARACTER)) {
523 				// Prevent SCE_C_STRINGEOL from leaking back to previous line which
524 				// ends with a line continuation by locking in the state upto this position.
525 				sc.SetState(sc.state);
526 			}
527 			// Reset states to begining of colourise so no surprises
528 			// if different sets of lines lexed.
529 			visibleChars = 0;
530 			lastWordWasUUID = false;
531 			isIncludePreprocessor = false;
532 			if (preproc.IsInactive()) {
533 				activitySet = activeFlag;
534 				sc.SetState(sc.state | activitySet);
535 			}
536 			if (activitySet) {
537 				if (sc.ch == '#') {
538 					if (sc.Match("#else") || sc.Match("#end") || sc.Match("#if")) {
539 						//activitySet = 0;
540 					}
541 				}
542 			}
543 		}
544 
545 		if (sc.atLineEnd) {
546 			lineCurrent++;
547 			vlls.Add(lineCurrent, preproc);
548 			if (rawStringTerminator != "") {
549 				rawSTNew.Set(lineCurrent-1, rawStringTerminator);
550 			}
551 		}
552 
553 		// Handle line continuation generically.
554 		if (sc.ch == '\\') {
555 			if (sc.chNext == '\n' || sc.chNext == '\r') {
556 				sc.Forward();
557 				if (sc.ch == '\r' && sc.chNext == '\n') {
558 					sc.Forward();
559 				}
560 				continuationLine = true;
561 				continue;
562 			}
563 		}
564 
565 		const bool atLineEndBeforeSwitch = sc.atLineEnd;
566 
567 		// Determine if the current state should terminate.
568 		switch (MaskActive(sc.state)) {
569 			case SCE_C_OPERATOR:
570 				sc.SetState(SCE_C_DEFAULT|activitySet);
571 				break;
572 			case SCE_C_NUMBER:
573 				// We accept almost anything because of hex. and number suffixes
574 				if (!(setWord.Contains(sc.ch) || ((sc.ch == '+' || sc.ch == '-') && (sc.chPrev == 'e' || sc.chPrev == 'E')))) {
575 					sc.SetState(SCE_C_DEFAULT|activitySet);
576 				}
577 				break;
578 			case SCE_C_IDENTIFIER:
579 				if (!setWord.Contains(sc.ch) || (sc.ch == '.')) {
580 					char s[1000];
581 					if (caseSensitive) {
582 						sc.GetCurrent(s, sizeof(s));
583 					} else {
584 						sc.GetCurrentLowered(s, sizeof(s));
585 					}
586 					if (keywords.InList(s)) {
587 						lastWordWasUUID = strcmp(s, "uuid") == 0;
588 						sc.ChangeState(SCE_C_WORD|activitySet);
589 					} else if (keywords2.InList(s)) {
590 						sc.ChangeState(SCE_C_WORD2|activitySet);
591 					} else if (keywords4.InList(s)) {
592 						sc.ChangeState(SCE_C_GLOBALCLASS|activitySet);
593 					}
594 					const bool literalString = sc.ch == '\"';
595 					if (literalString || sc.ch == '\'') {
596 						size_t lenS = strlen(s);
597 						const bool raw = literalString && sc.chPrev == 'R';
598 						if (raw)
599 							s[lenS--] = '\0';
600 						bool valid =
601 							(lenS == 0) ||
602 							((lenS == 1) && ((s[0] == 'L') || (s[0] == 'u') || (s[0] == 'U'))) ||
603 							((lenS == 2) && literalString && (s[0] == 'u') && (s[1] == '8'));
604 						if (valid) {
605 							if (literalString)
606 								sc.ChangeState((raw ? SCE_C_STRINGRAW : SCE_C_STRING)|activitySet);
607 							else
608 								sc.ChangeState(SCE_C_CHARACTER|activitySet);
609 						}
610 					}
611 					sc.SetState(SCE_C_DEFAULT|activitySet);
612 				}
613 				break;
614 			case SCE_C_PREPROCESSOR:
615 				if (sc.atLineStart && !continuationLine) {
616 					sc.SetState(SCE_C_DEFAULT|activitySet);
617 				} else if (options.stylingWithinPreprocessor) {
618 					if (IsASpace(sc.ch)) {
619 						sc.SetState(SCE_C_DEFAULT|activitySet);
620 					}
621 				} else {
622 					if (sc.Match('/', '*') || sc.Match('/', '/')) {
623 						sc.SetState(SCE_C_DEFAULT|activitySet);
624 					}
625 				}
626 				break;
627 			case SCE_C_COMMENT:
628 				if (sc.Match('*', '/')) {
629 					sc.Forward();
630 					sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
631 				}
632 				break;
633 			case SCE_C_COMMENTDOC:
634 				if (sc.Match('*', '/')) {
635 					sc.Forward();
636 					sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
637 				} else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support
638 					// Verify that we have the conditions to mark a comment-doc-keyword
639 					if ((IsASpace(sc.chPrev) || sc.chPrev == '*') && (!IsASpace(sc.chNext))) {
640 						styleBeforeDCKeyword = SCE_C_COMMENTDOC;
641 						sc.SetState(SCE_C_COMMENTDOCKEYWORD|activitySet);
642 					}
643 				}
644 				break;
645 			case SCE_C_COMMENTLINE:
646 				if (sc.atLineStart && !continuationLine) {
647 					sc.SetState(SCE_C_DEFAULT|activitySet);
648 				}
649 				break;
650 			case SCE_C_COMMENTLINEDOC:
651 				if (sc.atLineStart && !continuationLine) {
652 					sc.SetState(SCE_C_DEFAULT|activitySet);
653 				} else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support
654 					// Verify that we have the conditions to mark a comment-doc-keyword
655 					if ((IsASpace(sc.chPrev) || sc.chPrev == '/' || sc.chPrev == '!') && (!IsASpace(sc.chNext))) {
656 						styleBeforeDCKeyword = SCE_C_COMMENTLINEDOC;
657 						sc.SetState(SCE_C_COMMENTDOCKEYWORD|activitySet);
658 					}
659 				}
660 				break;
661 			case SCE_C_COMMENTDOCKEYWORD:
662 				if ((styleBeforeDCKeyword == SCE_C_COMMENTDOC) && sc.Match('*', '/')) {
663 					sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR);
664 					sc.Forward();
665 					sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
666 				} else if (!setDoxygen.Contains(sc.ch)) {
667 					char s[100];
668 					if (caseSensitive) {
669 						sc.GetCurrent(s, sizeof(s));
670 					} else {
671 						sc.GetCurrentLowered(s, sizeof(s));
672 					}
673 					if (!IsASpace(sc.ch) || !keywords3.InList(s + 1)) {
674 						sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR|activitySet);
675 					}
676 					sc.SetState(styleBeforeDCKeyword);
677 				}
678 				break;
679 			case SCE_C_STRING:
680 				if (sc.atLineEnd) {
681 					sc.ChangeState(SCE_C_STRINGEOL|activitySet);
682 				} else if (isIncludePreprocessor) {
683 					if (sc.ch == '>') {
684 						sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
685 						isIncludePreprocessor = false;
686 					}
687 				} else if (sc.ch == '\\') {
688 					if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
689 						sc.Forward();
690 					}
691 				} else if (sc.ch == '\"') {
692 					sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
693 				}
694 				break;
695 			case SCE_C_STRINGRAW:
696 				if (sc.Match(rawStringTerminator.c_str())) {
697 					for (size_t termPos=rawStringTerminator.size(); termPos; termPos--)
698 						sc.Forward();
699 					sc.SetState(SCE_C_DEFAULT|activitySet);
700 					rawStringTerminator = "";
701 				}
702 				break;
703 			case SCE_C_CHARACTER:
704 				if (sc.atLineEnd) {
705 					sc.ChangeState(SCE_C_STRINGEOL|activitySet);
706 				} else if (sc.ch == '\\') {
707 					if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
708 						sc.Forward();
709 					}
710 				} else if (sc.ch == '\'') {
711 					sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
712 				}
713 				break;
714 			case SCE_C_REGEX:
715 				if (sc.atLineStart) {
716 					sc.SetState(SCE_C_DEFAULT|activitySet);
717 				} else if (sc.ch == '/') {
718 					sc.Forward();
719 					while ((sc.ch < 0x80) && islower(sc.ch))
720 						sc.Forward();    // gobble regex flags
721 					sc.SetState(SCE_C_DEFAULT|activitySet);
722 				} else if (sc.ch == '\\') {
723 					// Gobble up the quoted character
724 					if (sc.chNext == '\\' || sc.chNext == '/') {
725 						sc.Forward();
726 					}
727 				}
728 				break;
729 			case SCE_C_STRINGEOL:
730 				if (sc.atLineStart) {
731 					sc.SetState(SCE_C_DEFAULT|activitySet);
732 				}
733 				break;
734 			case SCE_C_VERBATIM:
735 				if (sc.ch == '\"') {
736 					if (sc.chNext == '\"') {
737 						sc.Forward();
738 					} else {
739 						sc.ForwardSetState(SCE_C_DEFAULT|activitySet);
740 					}
741 				}
742 				break;
743 			case SCE_C_TRIPLEVERBATIM:
744 				if (sc.Match("\"\"\"")) {
745 					while (sc.Match('"')) {
746 						sc.Forward();
747 					}
748 					sc.SetState(SCE_C_DEFAULT|activitySet);
749 				}
750 				break;
751 			case SCE_C_UUID:
752 				if (sc.ch == '\r' || sc.ch == '\n' || sc.ch == ')') {
753 					sc.SetState(SCE_C_DEFAULT|activitySet);
754 				}
755 		}
756 
757 		if (sc.atLineEnd && !atLineEndBeforeSwitch) {
758 			// State exit processing consumed characters up to end of line.
759 			lineCurrent++;
760 			vlls.Add(lineCurrent, preproc);
761 		}
762 
763 		// Determine if a new state should be entered.
764 		if (MaskActive(sc.state) == SCE_C_DEFAULT) {
765 			if (sc.Match('@', '\"')) {
766 				sc.SetState(SCE_C_VERBATIM|activitySet);
767 				sc.Forward();
768 			} else if (options.triplequotedStrings && sc.Match("\"\"\"")) {
769 				sc.SetState(SCE_C_TRIPLEVERBATIM|activitySet);
770 				sc.Forward(2);
771 			} else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
772 				if (lastWordWasUUID) {
773 					sc.SetState(SCE_C_UUID|activitySet);
774 					lastWordWasUUID = false;
775 				} else {
776 					sc.SetState(SCE_C_NUMBER|activitySet);
777 				}
778 			} else if (setWordStart.Contains(sc.ch) || (sc.ch == '@')) {
779 				if (lastWordWasUUID) {
780 					sc.SetState(SCE_C_UUID|activitySet);
781 					lastWordWasUUID = false;
782 				} else {
783 					sc.SetState(SCE_C_IDENTIFIER|activitySet);
784 				}
785 			} else if (sc.Match('/', '*')) {
786 				if (sc.Match("/**") || sc.Match("/*!")) {	// Support of Qt/Doxygen doc. style
787 					sc.SetState(SCE_C_COMMENTDOC|activitySet);
788 				} else {
789 					sc.SetState(SCE_C_COMMENT|activitySet);
790 				}
791 				sc.Forward();	// Eat the * so it isn't used for the end of the comment
792 			} else if (sc.Match('/', '/')) {
793 				if ((sc.Match("///") && !sc.Match("////")) || sc.Match("//!"))
794 					// Support of Qt/Doxygen doc. style
795 					sc.SetState(SCE_C_COMMENTLINEDOC|activitySet);
796 				else
797 					sc.SetState(SCE_C_COMMENTLINE|activitySet);
798 			} else if (sc.ch == '/'
799 				   && (setOKBeforeRE.Contains(chPrevNonWhite)
800 				       || followsReturnKeyword(sc, styler))
801 				   && (!setCouldBePostOp.Contains(chPrevNonWhite)
802 				       || !FollowsPostfixOperator(sc, styler))) {
803 				sc.SetState(SCE_C_REGEX|activitySet);	// JavaScript's RegEx
804 			} else if (sc.ch == '\"') {
805 				if (sc.chPrev == 'R') {
806 					sc.SetState(SCE_C_STRINGRAW|activitySet);
807 					rawStringTerminator = ")";
808 					for (int termPos = sc.currentPos + 1;; termPos++) {
809 						char chTerminator = styler.SafeGetCharAt(termPos, '(');
810 						if (chTerminator == '(')
811 							break;
812 						rawStringTerminator += chTerminator;
813 					}
814 					rawStringTerminator += '\"';
815 				} else {
816 					sc.SetState(SCE_C_STRING|activitySet);
817 				}
818 				isIncludePreprocessor = false;	// ensure that '>' won't end the string
819 			} else if (isIncludePreprocessor && sc.ch == '<') {
820 				sc.SetState(SCE_C_STRING|activitySet);
821 			} else if (sc.ch == '\'') {
822 				sc.SetState(SCE_C_CHARACTER|activitySet);
823 			} else if (sc.ch == '#' && visibleChars == 0) {
824 				// Preprocessor commands are alone on their line
825 				sc.SetState(SCE_C_PREPROCESSOR|activitySet);
826 				// Skip whitespace between # and preprocessor word
827 				do {
828 					sc.Forward();
829 				} while ((sc.ch == ' ' || sc.ch == '\t') && sc.More());
830 				if (sc.atLineEnd) {
831 					sc.SetState(SCE_C_DEFAULT|activitySet);
832 				} else if (sc.Match("include")) {
833 					isIncludePreprocessor = true;
834 				} else {
835 					if (options.trackPreprocessor) {
836 						if (sc.Match("ifdef") || sc.Match("ifndef")) {
837 							bool isIfDef = sc.Match("ifdef");
838 							int i = isIfDef ? 5 : 6;
839 							std::string restOfLine = GetRestOfLine(styler, sc.currentPos + i + 1, false);
840 							bool foundDef = preprocessorDefinitions.find(restOfLine) != preprocessorDefinitions.end();
841 							preproc.StartSection(isIfDef == foundDef);
842 						} else if (sc.Match("if")) {
843 							std::string restOfLine = GetRestOfLine(styler, sc.currentPos + 2, true);
844 							bool ifGood = EvaluateExpression(restOfLine, preprocessorDefinitions);
845 							preproc.StartSection(ifGood);
846 						} else if (sc.Match("else")) {
847 							if (!preproc.CurrentIfTaken()) {
848 								preproc.InvertCurrentLevel();
849 								activitySet = preproc.IsInactive() ? activeFlag : 0;
850 								if (!activitySet)
851 									sc.ChangeState(SCE_C_PREPROCESSOR|activitySet);
852 							} else if (!preproc.IsInactive()) {
853 								preproc.InvertCurrentLevel();
854 								activitySet = preproc.IsInactive() ? activeFlag : 0;
855 								if (!activitySet)
856 									sc.ChangeState(SCE_C_PREPROCESSOR|activitySet);
857 							}
858 						} else if (sc.Match("elif")) {
859 							// Ensure only one chosen out of #if .. #elif .. #elif .. #else .. #endif
860 							if (!preproc.CurrentIfTaken()) {
861 								// Similar to #if
862 								std::string restOfLine = GetRestOfLine(styler, sc.currentPos + 2, true);
863 								bool ifGood = EvaluateExpression(restOfLine, preprocessorDefinitions);
864 								if (ifGood) {
865 									preproc.InvertCurrentLevel();
866 									activitySet = preproc.IsInactive() ? activeFlag : 0;
867 									if (!activitySet)
868 										sc.ChangeState(SCE_C_PREPROCESSOR|activitySet);
869 								}
870 							} else if (!preproc.IsInactive()) {
871 								preproc.InvertCurrentLevel();
872 								activitySet = preproc.IsInactive() ? activeFlag : 0;
873 								if (!activitySet)
874 									sc.ChangeState(SCE_C_PREPROCESSOR|activitySet);
875 							}
876 						} else if (sc.Match("endif")) {
877 							preproc.EndSection();
878 							activitySet = preproc.IsInactive() ? activeFlag : 0;
879 							sc.ChangeState(SCE_C_PREPROCESSOR|activitySet);
880 						} else if (sc.Match("define")) {
881 							if (options.updatePreprocessor && !preproc.IsInactive()) {
882 								std::string restOfLine = GetRestOfLine(styler, sc.currentPos + 6, true);
883 								if (restOfLine.find(")") == std::string::npos) {	// Don't handle macros with arguments
884 									std::vector<std::string> tokens = Tokenize(restOfLine);
885 									std::string key;
886 									std::string value("1");
887 									if (tokens.size() >= 1) {
888 										key = tokens[0];
889 										if (tokens.size() >= 2) {
890 											value = tokens[1];
891 										}
892 										preprocessorDefinitions[key] = value;
893 										ppDefineHistory.push_back(PPDefinition(lineCurrent, key, value));
894 										definitionsChanged = true;
895 									}
896 								}
897 							}
898 						}
899 					}
900 				}
901 			} else if (isoperator(static_cast<char>(sc.ch))) {
902 				sc.SetState(SCE_C_OPERATOR|activitySet);
903 			}
904 		}
905 
906 		if (!IsASpace(sc.ch) && !IsSpaceEquiv(sc.state)) {
907 			chPrevNonWhite = sc.ch;
908 			visibleChars++;
909 		}
910 		continuationLine = false;
911 	}
912 	const bool rawStringsChanged = rawStringTerminators.Merge(rawSTNew, lineCurrent);
913 	if (definitionsChanged || rawStringsChanged)
914 		styler.ChangeLexerState(startPos, startPos + length);
915 	sc.Complete();
916 }
917 
918 // Store both the current line's fold level and the next lines in the
919 // level store to make it easy to pick up with each increment
920 // and to make it possible to fiddle the current level for "} else {".
921 
Fold(unsigned int startPos,int length,int initStyle,IDocument * pAccess)922 void SCI_METHOD LexerCPP::Fold(unsigned int startPos, int length, int initStyle, IDocument *pAccess) {
923 
924 	if (!options.fold)
925 		return;
926 
927 	LexAccessor styler(pAccess);
928 
929 	unsigned int endPos = startPos + length;
930 	int visibleChars = 0;
931 	int lineCurrent = styler.GetLine(startPos);
932 	int levelCurrent = SC_FOLDLEVELBASE;
933 	if (lineCurrent > 0)
934 		levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
935 	int levelMinCurrent = levelCurrent;
936 	int levelNext = levelCurrent;
937 	char chNext = styler[startPos];
938 	int styleNext = MaskActive(styler.StyleAt(startPos));
939 	int style = MaskActive(initStyle);
940 	const bool userDefinedFoldMarkers = !options.foldExplicitStart.empty() && !options.foldExplicitEnd.empty();
941 	for (unsigned int i = startPos; i < endPos; i++) {
942 		char ch = chNext;
943 		chNext = styler.SafeGetCharAt(i + 1);
944 		int stylePrev = style;
945 		style = styleNext;
946 		styleNext = MaskActive(styler.StyleAt(i + 1));
947 		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
948 		if (options.foldComment && options.foldCommentMultiline && IsStreamCommentStyle(style)) {
949 			if (!IsStreamCommentStyle(stylePrev) && (stylePrev != SCE_C_COMMENTLINEDOC)) {
950 				levelNext++;
951 			} else if (!IsStreamCommentStyle(styleNext) && (styleNext != SCE_C_COMMENTLINEDOC) && !atEOL) {
952 				// Comments don't end at end of line and the next character may be unstyled.
953 				levelNext--;
954 			}
955 		}
956 		if (options.foldComment && options.foldCommentExplicit && ((style == SCE_C_COMMENTLINE) || options.foldExplicitAnywhere)) {
957 			if (userDefinedFoldMarkers) {
958 				if (styler.Match(i, options.foldExplicitStart.c_str())) {
959 					levelNext++;
960 				} else if (styler.Match(i, options.foldExplicitEnd.c_str())) {
961 					levelNext--;
962 				}
963 			} else {
964 				if ((ch == '/') && (chNext == '/')) {
965 					char chNext2 = styler.SafeGetCharAt(i + 2);
966 					if (chNext2 == '{') {
967 						levelNext++;
968 					} else if (chNext2 == '}') {
969 						levelNext--;
970 					}
971 				}
972 			}
973 		}
974 		if (options.foldPreprocessor && (style == SCE_C_PREPROCESSOR)) {
975 			if (ch == '#') {
976 				unsigned int j = i + 1;
977 				while ((j < endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) {
978 					j++;
979 				}
980 				if (styler.Match(j, "region") || styler.Match(j, "if")) {
981 					levelNext++;
982 				} else if (styler.Match(j, "end")) {
983 					levelNext--;
984 				}
985 			}
986 		}
987 		if (options.foldSyntaxBased && (style == SCE_C_OPERATOR)) {
988 			if (ch == '{') {
989 				// Measure the minimum before a '{' to allow
990 				// folding on "} else {"
991 				if (levelMinCurrent > levelNext) {
992 					levelMinCurrent = levelNext;
993 				}
994 				levelNext++;
995 			} else if (ch == '}') {
996 				levelNext--;
997 			}
998 		}
999 		if (!IsASpace(ch))
1000 			visibleChars++;
1001 		if (atEOL || (i == endPos-1)) {
1002 			int levelUse = levelCurrent;
1003 			if (options.foldSyntaxBased && options.foldAtElse) {
1004 				levelUse = levelMinCurrent;
1005 			}
1006 			int lev = levelUse | levelNext << 16;
1007 			if (visibleChars == 0 && options.foldCompact)
1008 				lev |= SC_FOLDLEVELWHITEFLAG;
1009 			if (levelUse < levelNext)
1010 				lev |= SC_FOLDLEVELHEADERFLAG;
1011 			if (lev != styler.LevelAt(lineCurrent)) {
1012 				styler.SetLevel(lineCurrent, lev);
1013 			}
1014 			lineCurrent++;
1015 			levelCurrent = levelNext;
1016 			levelMinCurrent = levelCurrent;
1017 			if (atEOL && (i == static_cast<unsigned int>(styler.Length()-1))) {
1018 				// There is an empty line at end of file so give it same level and empty
1019 				styler.SetLevel(lineCurrent, (levelCurrent | levelCurrent << 16) | SC_FOLDLEVELWHITEFLAG);
1020 			}
1021 			visibleChars = 0;
1022 		}
1023 	}
1024 }
1025 
EvaluateTokens(std::vector<std::string> & tokens)1026 void LexerCPP::EvaluateTokens(std::vector<std::string> &tokens) {
1027 
1028 	// Evaluate defined() statements to either 0 or 1
1029 	for (size_t i=0; (i+2)<tokens.size();) {
1030 		if ((tokens[i] == "defined") && (tokens[i+1] == "(")) {
1031 			const char *val = "0";
1032 			if (tokens[i+2] == ")") {
1033 				// defined()
1034 				tokens.erase(tokens.begin() + i + 1, tokens.begin() + i + 3);
1035 			} else if (((i+2)<tokens.size()) && (tokens[i+3] == ")")) {
1036 				// defined(<int>)
1037 				tokens.erase(tokens.begin() + i + 1, tokens.begin() + i + 4);
1038 				val = "1";
1039 			}
1040 			tokens[i] = val;
1041 		} else {
1042 			i++;
1043 		}
1044 	}
1045 
1046 	// Find bracketed subexpressions and recurse on them
1047 	std::vector<std::string>::iterator itBracket = std::find(tokens.begin(), tokens.end(), "(");
1048 	std::vector<std::string>::iterator itEndBracket = std::find(tokens.begin(), tokens.end(), ")");
1049 	while ((itBracket != tokens.end()) && (itEndBracket != tokens.end()) && (itEndBracket > itBracket)) {
1050 		std::vector<std::string> inBracket(itBracket + 1, itEndBracket);
1051 		EvaluateTokens(inBracket);
1052 
1053 		// The insertion is done before the removal because there were failures with the opposite approach
1054 		tokens.insert(itBracket, inBracket.begin(), inBracket.end());
1055 		itBracket = std::find(tokens.begin(), tokens.end(), "(");
1056 		itEndBracket = std::find(tokens.begin(), tokens.end(), ")");
1057 		tokens.erase(itBracket, itEndBracket + 1);
1058 
1059 		itBracket = std::find(tokens.begin(), tokens.end(), "(");
1060 		itEndBracket = std::find(tokens.begin(), tokens.end(), ")");
1061 	}
1062 
1063 	// Evaluate logical negations
1064 	for (size_t j=0; (j+1)<tokens.size();) {
1065 		if (setNegationOp.Contains(tokens[j][0])) {
1066 			int isTrue = atoi(tokens[j+1].c_str());
1067 			if (tokens[j] == "!")
1068 				isTrue = !isTrue;
1069 			std::vector<std::string>::iterator itInsert =
1070 				tokens.erase(tokens.begin() + j, tokens.begin() + j + 2);
1071 			tokens.insert(itInsert, isTrue ? "1" : "0");
1072 		} else {
1073 			j++;
1074 		}
1075 	}
1076 
1077 	// Evaluate expressions in precedence order
1078 	enum precedence { precArithmetic, precRelative, precLogical };
1079 	for (int prec=precArithmetic; prec <= precLogical; prec++) {
1080 		// Looking at 3 tokens at a time so end at 2 before end
1081 		for (size_t k=0; (k+2)<tokens.size();) {
1082 			char chOp = tokens[k+1][0];
1083 			if (
1084 				((prec==precArithmetic) && setArithmethicOp.Contains(chOp)) ||
1085 				((prec==precRelative) && setRelOp.Contains(chOp)) ||
1086 				((prec==precLogical) && setLogicalOp.Contains(chOp))
1087 				) {
1088 				int valA = atoi(tokens[k].c_str());
1089 				int valB = atoi(tokens[k+2].c_str());
1090 				int result = 0;
1091 				if (tokens[k+1] == "+")
1092 					result = valA + valB;
1093 				else if (tokens[k+1] == "-")
1094 					result = valA - valB;
1095 				else if (tokens[k+1] == "*")
1096 					result = valA * valB;
1097 				else if (tokens[k+1] == "/")
1098 					result = valA / (valB ? valB : 1);
1099 				else if (tokens[k+1] == "%")
1100 					result = valA % (valB ? valB : 1);
1101 				else if (tokens[k+1] == "<")
1102 					result = valA < valB;
1103 				else if (tokens[k+1] == "<=")
1104 					result = valA <= valB;
1105 				else if (tokens[k+1] == ">")
1106 					result = valA > valB;
1107 				else if (tokens[k+1] == ">=")
1108 					result = valA >= valB;
1109 				else if (tokens[k+1] == "==")
1110 					result = valA == valB;
1111 				else if (tokens[k+1] == "!=")
1112 					result = valA != valB;
1113 				else if (tokens[k+1] == "||")
1114 					result = valA || valB;
1115 				else if (tokens[k+1] == "&&")
1116 					result = valA && valB;
1117 				char sResult[30];
1118 				sprintf(sResult, "%d", result);
1119 				std::vector<std::string>::iterator itInsert =
1120 					tokens.erase(tokens.begin() + k, tokens.begin() + k + 3);
1121 				tokens.insert(itInsert, sResult);
1122 			} else {
1123 				k++;
1124 			}
1125 		}
1126 	}
1127 }
1128 
EvaluateExpression(const std::string & expr,const std::map<std::string,std::string> & preprocessorDefinitions)1129 bool LexerCPP::EvaluateExpression(const std::string &expr, const std::map<std::string, std::string> &preprocessorDefinitions) {
1130 	// Break into tokens, replacing with definitions
1131 	std::string word;
1132 	std::vector<std::string> tokens;
1133 	const char *cp = expr.c_str();
1134 	for (;;) {
1135 		if (setWord.Contains(*cp)) {
1136 			word += *cp;
1137 		} else {
1138 			std::map<std::string, std::string>::const_iterator it = preprocessorDefinitions.find(word);
1139 			if (it != preprocessorDefinitions.end()) {
1140 				tokens.push_back(it->second);
1141 			} else if (!word.empty() && ((word[0] >= '0' && word[0] <= '9') || (word == "defined"))) {
1142 				tokens.push_back(word);
1143 			}
1144 			word = "";
1145 			if (!*cp) {
1146 				break;
1147 			}
1148 			if ((*cp != ' ') && (*cp != '\t')) {
1149 				std::string op(cp, 1);
1150 				if (setRelOp.Contains(*cp)) {
1151 					if (setRelOp.Contains(cp[1])) {
1152 						op += cp[1];
1153 						cp++;
1154 					}
1155 				} else if (setLogicalOp.Contains(*cp)) {
1156 					if (setLogicalOp.Contains(cp[1])) {
1157 						op += cp[1];
1158 						cp++;
1159 					}
1160 				}
1161 				tokens.push_back(op);
1162 			}
1163 		}
1164 		cp++;
1165 	}
1166 
1167 	EvaluateTokens(tokens);
1168 
1169 	// "0" or "" -> false else true
1170 	bool isFalse = tokens.empty() ||
1171 		((tokens.size() == 1) && ((tokens[0] == "") || tokens[0] == "0"));
1172 	return !isFalse;
1173 }
1174 
1175 LexerModule lmCPP(SCLEX_CPP, LexerCPP::LexerFactoryCPP, "cpp", cppWordLists);
1176 LexerModule lmCPPNoCase(SCLEX_CPPNOCASE, LexerCPP::LexerFactoryCPPInsensitive, "cppnocase", cppWordLists);
1177