]> git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/LexPOV.cxx
b845b2d4720d23a61a38104fde26e844d859a3ef
[wxWidgets.git] / src / stc / scintilla / src / LexPOV.cxx
1 // Scintilla source code edit control
2 /** @file LexPOV.cxx
3 ** Lexer for POV-Ray SDL (Persistance of Vision Raytracer, Scene Description Language).
4 ** Written by Philippe Lhoste but this is mostly a derivative of LexCPP...
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 // Some points that distinguish from a simple C lexer:
10 // Identifiers start only by a character.
11 // No line continuation character.
12 // Strings are limited to 256 characters.
13 // Directives are similar to preprocessor commands,
14 // but we match directive keywords and colorize incorrect ones.
15 // Block comments can be nested (code stolen from my code in LexLua).
16
17 #include <stdlib.h>
18 #include <string.h>
19 #include <ctype.h>
20 #include <stdio.h>
21 #include <stdarg.h>
22
23 #include "Platform.h"
24
25 #include "PropSet.h"
26 #include "Accessor.h"
27 #include "StyleContext.h"
28 #include "KeyWords.h"
29 #include "Scintilla.h"
30 #include "SciLexer.h"
31
32 #ifdef SCI_NAMESPACE
33 using namespace Scintilla;
34 #endif
35
36 static inline bool IsAWordChar(int ch) {
37 return ch < 0x80 && (isalnum(ch) || ch == '_');
38 }
39
40 static inline bool IsAWordStart(int ch) {
41 return ch < 0x80 && isalpha(ch);
42 }
43
44 static inline bool IsANumberChar(int ch) {
45 // Not exactly following number definition (several dots are seen as OK, etc.)
46 // but probably enough in most cases.
47 return (ch < 0x80) &&
48 (isdigit(ch) || toupper(ch) == 'E' ||
49 ch == '.' || ch == '-' || ch == '+');
50 }
51
52 static void ColourisePovDoc(
53 unsigned int startPos,
54 int length,
55 int initStyle,
56 WordList *keywordlists[],
57 Accessor &styler) {
58
59 WordList &keywords1 = *keywordlists[0];
60 WordList &keywords2 = *keywordlists[1];
61 WordList &keywords3 = *keywordlists[2];
62 WordList &keywords4 = *keywordlists[3];
63 WordList &keywords5 = *keywordlists[4];
64 WordList &keywords6 = *keywordlists[5];
65 WordList &keywords7 = *keywordlists[6];
66 WordList &keywords8 = *keywordlists[7];
67
68 int currentLine = styler.GetLine(startPos);
69 // Initialize the block comment /* */ nesting level, if we are inside such a comment.
70 int blockCommentLevel = 0;
71 if (initStyle == SCE_POV_COMMENT) {
72 blockCommentLevel = styler.GetLineState(currentLine - 1);
73 }
74
75 // Do not leak onto next line
76 if (initStyle == SCE_POV_STRINGEOL || initStyle == SCE_POV_COMMENTLINE) {
77 initStyle = SCE_POV_DEFAULT;
78 }
79
80 short stringLen = 0;
81
82 StyleContext sc(startPos, length, initStyle, styler);
83
84 for (; sc.More(); sc.Forward()) {
85 if (sc.atLineEnd) {
86 // Update the line state, so it can be seen by next line
87 currentLine = styler.GetLine(sc.currentPos);
88 if (sc.state == SCE_POV_COMMENT) {
89 // Inside a block comment, we set the line state
90 styler.SetLineState(currentLine, blockCommentLevel);
91 } else {
92 // Reset the line state
93 styler.SetLineState(currentLine, 0);
94 }
95 }
96
97 if (sc.atLineStart && (sc.state == SCE_POV_STRING)) {
98 // Prevent SCE_POV_STRINGEOL from leaking back to previous line
99 sc.SetState(SCE_POV_STRING);
100 }
101
102 // Determine if the current state should terminate.
103 if (sc.state == SCE_POV_OPERATOR) {
104 sc.SetState(SCE_POV_DEFAULT);
105 } else if (sc.state == SCE_POV_NUMBER) {
106 // We stop the number definition on non-numerical non-dot non-eE non-sign char
107 if (!IsANumberChar(sc.ch)) {
108 sc.SetState(SCE_POV_DEFAULT);
109 }
110 } else if (sc.state == SCE_POV_IDENTIFIER) {
111 if (!IsAWordChar(sc.ch)) {
112 char s[100];
113 sc.GetCurrent(s, sizeof(s));
114 if (keywords2.InList(s)) {
115 sc.ChangeState(SCE_POV_WORD2);
116 } else if (keywords3.InList(s)) {
117 sc.ChangeState(SCE_POV_WORD3);
118 } else if (keywords4.InList(s)) {
119 sc.ChangeState(SCE_POV_WORD4);
120 } else if (keywords5.InList(s)) {
121 sc.ChangeState(SCE_POV_WORD5);
122 } else if (keywords6.InList(s)) {
123 sc.ChangeState(SCE_POV_WORD6);
124 } else if (keywords7.InList(s)) {
125 sc.ChangeState(SCE_POV_WORD7);
126 } else if (keywords8.InList(s)) {
127 sc.ChangeState(SCE_POV_WORD8);
128 }
129 sc.SetState(SCE_POV_DEFAULT);
130 }
131 } else if (sc.state == SCE_POV_DIRECTIVE) {
132 if (!IsAWordChar(sc.ch)) {
133 char s[100];
134 char *p;
135 sc.GetCurrent(s, sizeof(s));
136 p = s;
137 // Skip # and whitespace between # and directive word
138 do {
139 p++;
140 } while ((*p == ' ' || *p == '\t') && *p != '\0');
141 if (!keywords1.InList(p)) {
142 sc.ChangeState(SCE_POV_BADDIRECTIVE);
143 }
144 sc.SetState(SCE_POV_DEFAULT);
145 }
146 } else if (sc.state == SCE_POV_COMMENT) {
147 if (sc.Match('/', '*')) {
148 blockCommentLevel++;
149 sc.Forward();
150 } else if (sc.Match('*', '/') && blockCommentLevel > 0) {
151 blockCommentLevel--;
152 sc.Forward();
153 if (blockCommentLevel == 0) {
154 sc.ForwardSetState(SCE_POV_DEFAULT);
155 }
156 }
157 } else if (sc.state == SCE_POV_COMMENTLINE) {
158 if (sc.atLineEnd) {
159 sc.ForwardSetState(SCE_POV_DEFAULT);
160 }
161 } else if (sc.state == SCE_POV_STRING) {
162 if (sc.ch == '\\') {
163 stringLen++;
164 if (strchr("abfnrtuv0'\"", sc.chNext)) {
165 // Compound characters are counted as one.
166 // Note: for Unicode chars \u, we shouldn't count the next 4 digits...
167 sc.Forward();
168 }
169 } else if (sc.ch == '\"') {
170 sc.ForwardSetState(SCE_POV_DEFAULT);
171 } else if (sc.atLineEnd) {
172 sc.ChangeState(SCE_POV_STRINGEOL);
173 sc.ForwardSetState(SCE_POV_DEFAULT);
174 } else {
175 stringLen++;
176 }
177 if (stringLen > 256) {
178 // Strings are limited to 256 chars
179 sc.SetState(SCE_POV_STRINGEOL);
180 }
181 } else if (sc.state == SCE_POV_STRINGEOL) {
182 if (sc.ch == '\\') {
183 if (sc.chNext == '\"' || sc.chNext == '\\') {
184 sc.Forward();
185 }
186 } else if (sc.ch == '\"') {
187 sc.ForwardSetState(SCE_C_DEFAULT);
188 } else if (sc.atLineEnd) {
189 sc.ForwardSetState(SCE_POV_DEFAULT);
190 }
191 }
192
193 // Determine if a new state should be entered.
194 if (sc.state == SCE_POV_DEFAULT) {
195 if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
196 sc.SetState(SCE_POV_NUMBER);
197 } else if (IsAWordStart(sc.ch)) {
198 sc.SetState(SCE_POV_IDENTIFIER);
199 } else if (sc.Match('/', '*')) {
200 blockCommentLevel = 1;
201 sc.SetState(SCE_POV_COMMENT);
202 sc.Forward(); // Eat the * so it isn't used for the end of the comment
203 } else if (sc.Match('/', '/')) {
204 sc.SetState(SCE_POV_COMMENTLINE);
205 } else if (sc.ch == '\"') {
206 sc.SetState(SCE_POV_STRING);
207 stringLen = 0;
208 } else if (sc.ch == '#') {
209 sc.SetState(SCE_POV_DIRECTIVE);
210 // Skip whitespace between # and directive word
211 do {
212 sc.Forward();
213 } while ((sc.ch == ' ' || sc.ch == '\t') && sc.More());
214 if (sc.atLineEnd) {
215 sc.SetState(SCE_POV_DEFAULT);
216 }
217 } else if (isoperator(static_cast<char>(sc.ch))) {
218 sc.SetState(SCE_POV_OPERATOR);
219 }
220 }
221 }
222 sc.Complete();
223 }
224
225 static void FoldPovDoc(
226 unsigned int startPos,
227 int length,
228 int initStyle,
229 WordList *[],
230 Accessor &styler) {
231
232 bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
233 bool foldDirective = styler.GetPropertyInt("fold.directive") != 0;
234 bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
235 unsigned int endPos = startPos + length;
236 int visibleChars = 0;
237 int lineCurrent = styler.GetLine(startPos);
238 int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
239 int levelCurrent = levelPrev;
240 char chNext = styler[startPos];
241 int styleNext = styler.StyleAt(startPos);
242 int style = initStyle;
243 for (unsigned int i = startPos; i < endPos; i++) {
244 char ch = chNext;
245 chNext = styler.SafeGetCharAt(i + 1);
246 int stylePrev = style;
247 style = styleNext;
248 styleNext = styler.StyleAt(i + 1);
249 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
250 if (foldComment && (style == SCE_POV_COMMENT)) {
251 if (stylePrev != SCE_POV_COMMENT) {
252 levelCurrent++;
253 } else if ((styleNext != SCE_POV_COMMENT) && !atEOL) {
254 // Comments don't end at end of line and the next character may be unstyled.
255 levelCurrent--;
256 }
257 }
258 if (foldComment && (style == SCE_POV_COMMENTLINE)) {
259 if ((ch == '/') && (chNext == '/')) {
260 char chNext2 = styler.SafeGetCharAt(i + 2);
261 if (chNext2 == '{') {
262 levelCurrent++;
263 } else if (chNext2 == '}') {
264 levelCurrent--;
265 }
266 }
267 }
268 if (foldDirective && (style == SCE_POV_DIRECTIVE)) {
269 if (ch == '#') {
270 unsigned int j=i+1;
271 while ((j<endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) {
272 j++;
273 }
274 }
275 }
276 if (style == SCE_POV_OPERATOR) {
277 if (ch == '{') {
278 levelCurrent++;
279 } else if (ch == '}') {
280 levelCurrent--;
281 }
282 }
283 if (atEOL) {
284 int lev = levelPrev;
285 if (visibleChars == 0 && foldCompact)
286 lev |= SC_FOLDLEVELWHITEFLAG;
287 if ((levelCurrent > levelPrev) && (visibleChars > 0))
288 lev |= SC_FOLDLEVELHEADERFLAG;
289 if (lev != styler.LevelAt(lineCurrent)) {
290 styler.SetLevel(lineCurrent, lev);
291 }
292 lineCurrent++;
293 levelPrev = levelCurrent;
294 visibleChars = 0;
295 }
296 if (!isspacechar(ch))
297 visibleChars++;
298 }
299 // Fill in the real level of the next line, keeping the current flags as they will be filled in later
300 int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
301 styler.SetLevel(lineCurrent, levelPrev | flagsNext);
302 }
303
304 static const char * const povWordLists[] = {
305 "Language directives",
306 "Objects & CSG & Appearance",
307 "Types & Modifiers & Items",
308 "Predefined Identifiers",
309 "Predefined Functions",
310 "User defined 1",
311 "User defined 2",
312 "User defined 3",
313 0,
314 };
315
316 LexerModule lmPOV(SCLEX_POV, ColourisePovDoc, "pov", FoldPovDoc, povWordLists);