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