]>
Commit | Line | Data |
---|---|---|
1dcf666d RD |
1 | // Scintilla source code edit control |
2 | /** @file LexVisualProlog.cxx | |
3 | ** Lexer for Visual Prolog. | |
4 | **/ | |
5 | // Author Thomas Linder Puls, Prolog Development Denter A/S, http://www.visual-prolog.com | |
6 | // Based on Lexer for C++, C, Java, and JavaScript. | |
7 | // Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org> | |
8 | // The License.txt file describes the conditions under which this software may be distributed. | |
9 | ||
10 | #include <stdlib.h> | |
11 | #include <string.h> | |
12 | #include <ctype.h> | |
13 | #include <stdio.h> | |
14 | #include <stdarg.h> | |
15 | #include <assert.h> | |
16 | ||
17 | #ifdef _MSC_VER | |
18 | #pragma warning(disable: 4786) | |
19 | #endif | |
20 | ||
21 | #include <string> | |
22 | #include <vector> | |
23 | #include <map> | |
24 | #include <algorithm> | |
25 | ||
26 | #include "ILexer.h" | |
27 | #include "Scintilla.h" | |
28 | #include "SciLexer.h" | |
29 | ||
30 | #include "WordList.h" | |
31 | #include "LexAccessor.h" | |
32 | #include "Accessor.h" | |
33 | #include "StyleContext.h" | |
34 | #include "CharacterSet.h" | |
35 | #include "LexerModule.h" | |
36 | #include "OptionSet.h" | |
37 | ||
38 | #ifdef SCI_NAMESPACE | |
39 | using namespace Scintilla; | |
40 | #endif | |
41 | ||
42 | // Options used for LexerVisualProlog | |
43 | struct OptionsVisualProlog { | |
44 | OptionsVisualProlog() { | |
45 | } | |
46 | }; | |
47 | ||
48 | static const char *const visualPrologWordLists[] = { | |
49 | "Major keywords (class, predicates, ...)", | |
50 | "Minor keywords (if, then, try, ...)", | |
51 | "Directive keywords without the '#' (include, requires, ...)", | |
52 | "Documentation keywords without the '@' (short, detail, ...)", | |
53 | 0, | |
54 | }; | |
55 | ||
56 | struct OptionSetVisualProlog : public OptionSet<OptionsVisualProlog> { | |
57 | OptionSetVisualProlog() { | |
58 | DefineWordListSets(visualPrologWordLists); | |
59 | } | |
60 | }; | |
61 | ||
62 | class LexerVisualProlog : public ILexer { | |
63 | WordList majorKeywords; | |
64 | WordList minorKeywords; | |
65 | WordList directiveKeywords; | |
66 | WordList docKeywords; | |
67 | OptionsVisualProlog options; | |
68 | OptionSetVisualProlog osVisualProlog; | |
69 | public: | |
70 | LexerVisualProlog() { | |
71 | } | |
72 | virtual ~LexerVisualProlog() { | |
73 | } | |
74 | void SCI_METHOD Release() { | |
75 | delete this; | |
76 | } | |
77 | int SCI_METHOD Version() const { | |
78 | return lvOriginal; | |
79 | } | |
80 | const char * SCI_METHOD PropertyNames() { | |
81 | return osVisualProlog.PropertyNames(); | |
82 | } | |
83 | int SCI_METHOD PropertyType(const char *name) { | |
84 | return osVisualProlog.PropertyType(name); | |
85 | } | |
86 | const char * SCI_METHOD DescribeProperty(const char *name) { | |
87 | return osVisualProlog.DescribeProperty(name); | |
88 | } | |
89 | int SCI_METHOD PropertySet(const char *key, const char *val); | |
90 | const char * SCI_METHOD DescribeWordListSets() { | |
91 | return osVisualProlog.DescribeWordListSets(); | |
92 | } | |
93 | int SCI_METHOD WordListSet(int n, const char *wl); | |
94 | void SCI_METHOD Lex(unsigned int startPos, int length, int initStyle, IDocument *pAccess); | |
95 | void SCI_METHOD Fold(unsigned int startPos, int length, int initStyle, IDocument *pAccess); | |
96 | ||
97 | void * SCI_METHOD PrivateCall(int, void *) { | |
98 | return 0; | |
99 | } | |
100 | ||
101 | static ILexer *LexerFactoryVisualProlog() { | |
102 | return new LexerVisualProlog(); | |
103 | } | |
104 | }; | |
105 | ||
106 | int SCI_METHOD LexerVisualProlog::PropertySet(const char *key, const char *val) { | |
107 | if (osVisualProlog.PropertySet(&options, key, val)) { | |
108 | return 0; | |
109 | } | |
110 | return -1; | |
111 | } | |
112 | ||
113 | int SCI_METHOD LexerVisualProlog::WordListSet(int n, const char *wl) { | |
114 | WordList *wordListN = 0; | |
115 | switch (n) { | |
116 | case 0: | |
117 | wordListN = &majorKeywords; | |
118 | break; | |
119 | case 1: | |
120 | wordListN = &minorKeywords; | |
121 | break; | |
122 | case 2: | |
123 | wordListN = &directiveKeywords; | |
124 | break; | |
125 | case 3: | |
126 | wordListN = &docKeywords; | |
127 | break; | |
128 | } | |
129 | int firstModification = -1; | |
130 | if (wordListN) { | |
131 | WordList wlNew; | |
132 | wlNew.Set(wl); | |
133 | if (*wordListN != wlNew) { | |
134 | wordListN->Set(wl); | |
135 | firstModification = 0; | |
136 | } | |
137 | } | |
138 | return firstModification; | |
139 | } | |
140 | ||
141 | // Functor used to truncate history | |
142 | struct After { | |
143 | int line; | |
144 | After(int line_) : line(line_) {} | |
145 | }; | |
146 | ||
147 | // Look ahead to see which colour "end" should have (takes colour after the following keyword) | |
148 | static void endLookAhead(char s[], LexAccessor &styler, int start, CharacterSet &setIdentifier) { | |
149 | char ch = styler.SafeGetCharAt(start, '\n'); | |
150 | while (' ' == ch) { | |
151 | start++; | |
152 | ch = styler.SafeGetCharAt(start, '\n'); | |
153 | } | |
154 | int i = 0; | |
155 | while (i < 100 && setIdentifier.Contains(ch)){ | |
156 | s[i] = ch; | |
157 | i++; | |
158 | ch = styler.SafeGetCharAt(start + i, '\n'); | |
159 | } | |
160 | s[i] = '\0'; | |
161 | } | |
162 | ||
163 | static void forwardEscapeLiteral(StyleContext &sc, int OwnChar, int EscapeState) { | |
164 | sc.Forward(); | |
165 | if (sc.ch == OwnChar || sc.ch == '\\' || sc.ch == 'n' || sc.ch == 'l' || sc.ch == 'r' || sc.ch == 't') { | |
166 | sc.ChangeState(EscapeState); | |
167 | } else if (sc.ch == 'u') { | |
168 | if (IsADigit(sc.chNext, 16)) { | |
169 | sc.Forward(); | |
170 | if (IsADigit(sc.chNext, 16)) { | |
171 | sc.Forward(); | |
172 | if (IsADigit(sc.chNext, 16)) { | |
173 | sc.Forward(); | |
174 | if (IsADigit(sc.chNext, 16)) { | |
175 | sc.Forward(); | |
176 | sc.ChangeState(EscapeState); | |
177 | } | |
178 | } | |
179 | } | |
180 | } | |
181 | } | |
182 | } | |
183 | ||
184 | void SCI_METHOD LexerVisualProlog::Lex(unsigned int startPos, int length, int initStyle, IDocument *pAccess) { | |
185 | LexAccessor styler(pAccess); | |
186 | ||
187 | CharacterSet setDoxygen(CharacterSet::setAlpha, "$@\\&<>#{}[]"); | |
188 | ||
189 | CharacterSet setLowerStart(CharacterSet::setLower); | |
190 | CharacterSet setVariableStart(CharacterSet::setUpper); | |
191 | CharacterSet setIdentifier(CharacterSet::setAlphaNum, "_", 0x80, true); | |
192 | ||
193 | int styleBeforeDocKeyword = SCE_VISUALPROLOG_DEFAULT; | |
194 | ||
195 | int currentLine = styler.GetLine(startPos); | |
196 | ||
197 | int nestLevel = 0; | |
198 | if (currentLine >= 1) | |
199 | { | |
200 | nestLevel = styler.GetLineState(currentLine - 1); | |
201 | } | |
202 | ||
203 | StyleContext sc(startPos, length, initStyle, styler, 0x7f); | |
204 | ||
205 | // Truncate ppDefineHistory before current line | |
206 | ||
207 | for (; sc.More(); sc.Forward()) { | |
208 | ||
209 | if (sc.atLineEnd) { | |
210 | // Update the line state, so it can be seen by next line | |
211 | styler.SetLineState(currentLine, nestLevel); | |
212 | currentLine++; | |
213 | } | |
214 | ||
215 | if (sc.atLineStart) { | |
216 | if ((sc.state == SCE_VISUALPROLOG_STRING) || (sc.state == SCE_VISUALPROLOG_CHARACTER)) { | |
217 | // Prevent SCE_VISUALPROLOG_STRING_EOL from leaking back to previous line which | |
218 | // ends with a line continuation by locking in the state upto this position. | |
219 | sc.SetState(sc.state); | |
220 | } | |
221 | } | |
222 | ||
223 | const bool atLineEndBeforeSwitch = sc.atLineEnd; | |
224 | ||
225 | // Determine if the current state should terminate. | |
226 | switch (sc.state) { | |
227 | case SCE_VISUALPROLOG_OPERATOR: | |
228 | sc.SetState(SCE_VISUALPROLOG_DEFAULT); | |
229 | break; | |
230 | case SCE_VISUALPROLOG_NUMBER: | |
231 | // We accept almost anything because of hex. and number suffixes | |
232 | if (!(setIdentifier.Contains(sc.ch) || (sc.ch == '.') || ((sc.ch == '+' || sc.ch == '-') && (sc.chPrev == 'e' || sc.chPrev == 'E')))) { | |
233 | sc.SetState(SCE_VISUALPROLOG_DEFAULT); | |
234 | } | |
235 | break; | |
236 | case SCE_VISUALPROLOG_IDENTIFIER: | |
237 | if (!setIdentifier.Contains(sc.ch)) { | |
238 | char s[1000]; | |
239 | sc.GetCurrent(s, sizeof(s)); | |
240 | if (0 == strcmp(s, "end")) { | |
241 | endLookAhead(s, styler, sc.currentPos, setIdentifier); | |
242 | } | |
243 | if (majorKeywords.InList(s)) { | |
244 | sc.ChangeState(SCE_VISUALPROLOG_KEY_MAJOR); | |
245 | } else if (minorKeywords.InList(s)) { | |
246 | sc.ChangeState(SCE_VISUALPROLOG_KEY_MINOR); | |
247 | } | |
248 | sc.SetState(SCE_VISUALPROLOG_DEFAULT); | |
249 | } | |
250 | break; | |
251 | case SCE_VISUALPROLOG_VARIABLE: | |
252 | case SCE_VISUALPROLOG_ANONYMOUS: | |
253 | if (!setIdentifier.Contains(sc.ch)) { | |
254 | sc.SetState(SCE_VISUALPROLOG_DEFAULT); | |
255 | } | |
256 | break; | |
257 | case SCE_VISUALPROLOG_KEY_DIRECTIVE: | |
258 | if (!setLowerStart.Contains(sc.ch)) { | |
259 | char s[1000]; | |
260 | sc.GetCurrent(s, sizeof(s)); | |
261 | if (!directiveKeywords.InList(s+1)) { | |
262 | sc.ChangeState(SCE_VISUALPROLOG_IDENTIFIER); | |
263 | } | |
264 | sc.SetState(SCE_VISUALPROLOG_DEFAULT); | |
265 | } | |
266 | break; | |
267 | case SCE_VISUALPROLOG_COMMENT_BLOCK: | |
268 | if (sc.Match('*', '/')) { | |
269 | sc.Forward(); | |
270 | nestLevel--; | |
271 | int nextState = (nestLevel == 0) ? SCE_VISUALPROLOG_DEFAULT : SCE_VISUALPROLOG_COMMENT_BLOCK; | |
272 | sc.ForwardSetState(nextState); | |
273 | } else if (sc.Match('/', '*')) { | |
274 | sc.Forward(); | |
275 | nestLevel++; | |
276 | } else if (sc.ch == '%') { | |
277 | sc.SetState(SCE_VISUALPROLOG_COMMENT_LINE); | |
278 | } else if (sc.ch == '@') { | |
279 | styleBeforeDocKeyword = sc.state; | |
280 | sc.SetState(SCE_VISUALPROLOG_COMMENT_KEY_ERROR); | |
281 | } | |
282 | break; | |
283 | case SCE_VISUALPROLOG_COMMENT_LINE: | |
284 | if (sc.atLineEnd) { | |
285 | int nextState = (nestLevel == 0) ? SCE_VISUALPROLOG_DEFAULT : SCE_VISUALPROLOG_COMMENT_BLOCK; | |
286 | sc.SetState(nextState); | |
287 | } else if (sc.ch == '@') { | |
288 | styleBeforeDocKeyword = sc.state; | |
289 | sc.SetState(SCE_VISUALPROLOG_COMMENT_KEY_ERROR); | |
290 | } | |
291 | break; | |
292 | case SCE_VISUALPROLOG_COMMENT_KEY_ERROR: | |
293 | if (!setDoxygen.Contains(sc.ch)) { | |
294 | char s[1000]; | |
295 | sc.GetCurrent(s, sizeof(s)); | |
296 | if (docKeywords.InList(s+1)) { | |
297 | sc.ChangeState(SCE_VISUALPROLOG_COMMENT_KEY); | |
298 | } | |
299 | sc.SetState(styleBeforeDocKeyword); | |
300 | } | |
301 | if (SCE_VISUALPROLOG_COMMENT_LINE == styleBeforeDocKeyword && sc.atLineStart) { | |
302 | sc.SetState(SCE_VISUALPROLOG_DEFAULT); | |
303 | } else if (SCE_VISUALPROLOG_COMMENT_BLOCK == styleBeforeDocKeyword && sc.atLineStart) { | |
304 | sc.SetState(SCE_VISUALPROLOG_COMMENT_BLOCK); | |
305 | } | |
306 | break; | |
307 | case SCE_VISUALPROLOG_STRING_ESCAPE: | |
308 | case SCE_VISUALPROLOG_STRING_ESCAPE_ERROR: | |
309 | // return to SCE_VISUALPROLOG_STRING and treat as such (fall-through) | |
310 | sc.SetState(SCE_VISUALPROLOG_STRING); | |
311 | case SCE_VISUALPROLOG_STRING: | |
312 | if (sc.atLineEnd) { | |
313 | sc.SetState(SCE_VISUALPROLOG_STRING_EOL_OPEN); | |
314 | } else if (sc.ch == '"') { | |
315 | sc.ForwardSetState(SCE_VISUALPROLOG_DEFAULT); | |
316 | } else if (sc.ch == '\\') { | |
317 | sc.SetState(SCE_VISUALPROLOG_STRING_ESCAPE_ERROR); | |
318 | forwardEscapeLiteral(sc, '"', SCE_VISUALPROLOG_STRING_ESCAPE); | |
319 | } | |
320 | break; | |
321 | case SCE_VISUALPROLOG_CHARACTER_TOO_MANY: | |
322 | if (sc.atLineStart) { | |
323 | sc.SetState(SCE_VISUALPROLOG_DEFAULT); | |
324 | } else if (sc.ch == '\'') { | |
325 | sc.SetState(SCE_VISUALPROLOG_CHARACTER); | |
326 | sc.ForwardSetState(SCE_VISUALPROLOG_DEFAULT); | |
327 | } | |
328 | break; | |
329 | case SCE_VISUALPROLOG_CHARACTER: | |
330 | if (sc.atLineEnd) { | |
331 | sc.SetState(SCE_VISUALPROLOG_STRING_EOL_OPEN); // reuse STRING_EOL_OPEN for this | |
332 | } else if (sc.ch == '\'') { | |
333 | sc.SetState(SCE_VISUALPROLOG_CHARACTER_ESCAPE_ERROR); | |
334 | sc.ForwardSetState(SCE_VISUALPROLOG_DEFAULT); | |
335 | } else { | |
336 | if (sc.ch == '\\') { | |
337 | sc.SetState(SCE_VISUALPROLOG_CHARACTER_ESCAPE_ERROR); | |
338 | forwardEscapeLiteral(sc, '\'', SCE_VISUALPROLOG_CHARACTER); | |
339 | } | |
340 | sc.ForwardSetState(SCE_VISUALPROLOG_CHARACTER); | |
341 | if (sc.ch == '\'') { | |
342 | sc.ForwardSetState(SCE_VISUALPROLOG_DEFAULT); | |
343 | } else { | |
344 | sc.SetState(SCE_VISUALPROLOG_CHARACTER_TOO_MANY); | |
345 | } | |
346 | } | |
347 | break; | |
348 | case SCE_VISUALPROLOG_STRING_EOL_OPEN: | |
349 | if (sc.atLineStart) { | |
350 | sc.SetState(SCE_VISUALPROLOG_DEFAULT); | |
351 | } | |
352 | break; | |
353 | case SCE_VISUALPROLOG_STRING_VERBATIM_SPECIAL: | |
354 | case SCE_VISUALPROLOG_STRING_VERBATIM_EOL: | |
355 | // return to SCE_VISUALPROLOG_STRING_VERBATIM and treat as such (fall-through) | |
356 | sc.SetState(SCE_VISUALPROLOG_STRING_VERBATIM); | |
357 | case SCE_VISUALPROLOG_STRING_VERBATIM: | |
358 | if (sc.atLineEnd) { | |
359 | sc.SetState(SCE_VISUALPROLOG_STRING_VERBATIM_EOL); | |
360 | } else if (sc.ch == '\"') { | |
361 | if (sc.chNext == '\"') { | |
362 | sc.SetState(SCE_VISUALPROLOG_STRING_VERBATIM_SPECIAL); | |
363 | sc.Forward(); | |
364 | } else { | |
365 | sc.ForwardSetState(SCE_VISUALPROLOG_DEFAULT); | |
366 | } | |
367 | } | |
368 | break; | |
369 | } | |
370 | ||
371 | if (sc.atLineEnd && !atLineEndBeforeSwitch) { | |
372 | // State exit processing consumed characters up to end of line. | |
373 | currentLine++; | |
374 | } | |
375 | ||
376 | // Determine if a new state should be entered. | |
377 | if (sc.state == SCE_VISUALPROLOG_DEFAULT) { | |
378 | if (sc.Match('@', '\"')) { | |
379 | sc.SetState(SCE_VISUALPROLOG_STRING_VERBATIM); | |
380 | sc.Forward(); | |
381 | } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) { | |
382 | sc.SetState(SCE_VISUALPROLOG_NUMBER); | |
383 | } else if (setLowerStart.Contains(sc.ch)) { | |
384 | sc.SetState(SCE_VISUALPROLOG_IDENTIFIER); | |
385 | } else if (setVariableStart.Contains(sc.ch)) { | |
386 | sc.SetState(SCE_VISUALPROLOG_VARIABLE); | |
387 | } else if (sc.ch == '_') { | |
388 | sc.SetState(SCE_VISUALPROLOG_ANONYMOUS); | |
389 | } else if (sc.Match('/', '*')) { | |
390 | sc.SetState(SCE_VISUALPROLOG_COMMENT_BLOCK); | |
391 | nestLevel = 1; | |
392 | sc.Forward(); // Eat the * so it isn't used for the end of the comment | |
393 | } else if (sc.ch == '%') { | |
394 | sc.SetState(SCE_VISUALPROLOG_COMMENT_LINE); | |
395 | } else if (sc.ch == '\"') { | |
396 | sc.SetState(SCE_VISUALPROLOG_STRING); | |
397 | } else if (sc.ch == '\'') { | |
398 | sc.SetState(SCE_VISUALPROLOG_CHARACTER); | |
399 | } else if (sc.ch == '#') { | |
400 | sc.SetState(SCE_VISUALPROLOG_KEY_DIRECTIVE); | |
401 | } else if (isoperator(static_cast<char>(sc.ch)) || sc.ch == '\\') { | |
402 | sc.SetState(SCE_VISUALPROLOG_OPERATOR); | |
403 | } | |
404 | } | |
405 | ||
406 | } | |
407 | sc.Complete(); | |
408 | styler.Flush(); | |
409 | } | |
410 | ||
411 | // Store both the current line's fold level and the next lines in the | |
412 | // level store to make it easy to pick up with each increment | |
413 | // and to make it possible to fiddle the current level for "} else {". | |
414 | ||
415 | void SCI_METHOD LexerVisualProlog::Fold(unsigned int startPos, int length, int initStyle, IDocument *pAccess) { | |
416 | ||
417 | LexAccessor styler(pAccess); | |
418 | ||
419 | unsigned int endPos = startPos + length; | |
420 | int visibleChars = 0; | |
421 | int currentLine = styler.GetLine(startPos); | |
422 | int levelCurrent = SC_FOLDLEVELBASE; | |
423 | if (currentLine > 0) | |
424 | levelCurrent = styler.LevelAt(currentLine-1) >> 16; | |
425 | int levelMinCurrent = levelCurrent; | |
426 | int levelNext = levelCurrent; | |
427 | char chNext = styler[startPos]; | |
428 | int styleNext = styler.StyleAt(startPos); | |
429 | int style = initStyle; | |
430 | for (unsigned int i = startPos; i < endPos; i++) { | |
431 | char ch = chNext; | |
432 | chNext = styler.SafeGetCharAt(i + 1); | |
433 | style = styleNext; | |
434 | styleNext = styler.StyleAt(i + 1); | |
435 | bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); | |
436 | if (style == SCE_VISUALPROLOG_OPERATOR) { | |
437 | if (ch == '{') { | |
438 | // Measure the minimum before a '{' to allow | |
439 | // folding on "} else {" | |
440 | if (levelMinCurrent > levelNext) { | |
441 | levelMinCurrent = levelNext; | |
442 | } | |
443 | levelNext++; | |
444 | } else if (ch == '}') { | |
445 | levelNext--; | |
446 | } | |
447 | } | |
448 | if (!IsASpace(ch)) | |
449 | visibleChars++; | |
450 | if (atEOL || (i == endPos-1)) { | |
451 | int levelUse = levelCurrent; | |
452 | int lev = levelUse | levelNext << 16; | |
453 | if (levelUse < levelNext) | |
454 | lev |= SC_FOLDLEVELHEADERFLAG; | |
455 | if (lev != styler.LevelAt(currentLine)) { | |
456 | styler.SetLevel(currentLine, lev); | |
457 | } | |
458 | currentLine++; | |
459 | levelCurrent = levelNext; | |
460 | levelMinCurrent = levelCurrent; | |
461 | if (atEOL && (i == static_cast<unsigned int>(styler.Length()-1))) { | |
462 | // There is an empty line at end of file so give it same level and empty | |
463 | styler.SetLevel(currentLine, (levelCurrent | levelCurrent << 16) | SC_FOLDLEVELWHITEFLAG); | |
464 | } | |
465 | visibleChars = 0; | |
466 | } | |
467 | } | |
468 | } | |
469 | ||
470 | LexerModule lmVisualProlog(SCLEX_VISUALPROLOG, LexerVisualProlog::LexerFactoryVisualProlog, "visualprolog", visualPrologWordLists); |