1
// Scintilla source code edit control
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...
6
// Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
7
// The License.txt file describes the conditions under which this software may be distributed.
9
// 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).
27
#include "StyleContext.h"
29
#include "Scintilla.h"
33
using namespace Scintilla;
36
static inline bool IsAWordChar(int ch) {
37
return ch < 0x80 && (isalnum(ch) || ch == '_');
40
static inline bool IsAWordStart(int ch) {
41
return ch < 0x80 && isalpha(ch);
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.
48
(isdigit(ch) || toupper(ch) == 'E' ||
49
ch == '.' || ch == '-' || ch == '+');
52
static void ColourisePovDoc(
53
unsigned int startPos,
56
WordList *keywordlists[],
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];
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);
75
// Do not leak onto next line
76
if (initStyle == SCE_POV_STRINGEOL || initStyle == SCE_POV_COMMENTLINE) {
77
initStyle = SCE_POV_DEFAULT;
82
StyleContext sc(startPos, length, initStyle, styler);
84
for (; sc.More(); sc.Forward()) {
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);
92
// Reset the line state
93
styler.SetLineState(currentLine, 0);
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);
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);
110
} else if (sc.state == SCE_POV_IDENTIFIER) {
111
if (!IsAWordChar(sc.ch)) {
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);
129
sc.SetState(SCE_POV_DEFAULT);
131
} else if (sc.state == SCE_POV_DIRECTIVE) {
132
if (!IsAWordChar(sc.ch)) {
135
sc.GetCurrent(s, sizeof(s));
137
// Skip # and whitespace between # and directive word
140
} while ((*p == ' ' || *p == '\t') && *p != '\0');
141
if (!keywords1.InList(p)) {
142
sc.ChangeState(SCE_POV_BADDIRECTIVE);
144
sc.SetState(SCE_POV_DEFAULT);
146
} else if (sc.state == SCE_POV_COMMENT) {
147
if (sc.Match('/', '*')) {
150
} else if (sc.Match('*', '/') && blockCommentLevel > 0) {
153
if (blockCommentLevel == 0) {
154
sc.ForwardSetState(SCE_POV_DEFAULT);
157
} else if (sc.state == SCE_POV_COMMENTLINE) {
159
sc.ForwardSetState(SCE_POV_DEFAULT);
161
} else if (sc.state == SCE_POV_STRING) {
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...
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);
177
if (stringLen > 256) {
178
// Strings are limited to 256 chars
179
sc.SetState(SCE_POV_STRINGEOL);
181
} else if (sc.state == SCE_POV_STRINGEOL) {
183
if (sc.chNext == '\"' || sc.chNext == '\\') {
186
} else if (sc.ch == '\"') {
187
sc.ForwardSetState(SCE_C_DEFAULT);
188
} else if (sc.atLineEnd) {
189
sc.ForwardSetState(SCE_POV_DEFAULT);
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);
208
} else if (sc.ch == '#') {
209
sc.SetState(SCE_POV_DIRECTIVE);
210
// Skip whitespace between # and directive word
213
} while ((sc.ch == ' ' || sc.ch == '\t') && sc.More());
215
sc.SetState(SCE_POV_DEFAULT);
217
} else if (isoperator(static_cast<char>(sc.ch))) {
218
sc.SetState(SCE_POV_OPERATOR);
225
static void FoldPovDoc(
226
unsigned int startPos,
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++) {
245
chNext = styler.SafeGetCharAt(i + 1);
246
int stylePrev = style;
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) {
253
} else if ((styleNext != SCE_POV_COMMENT) && !atEOL) {
254
// Comments don't end at end of line and the next character may be unstyled.
258
if (foldComment && (style == SCE_POV_COMMENTLINE)) {
259
if ((ch == '/') && (chNext == '/')) {
260
char chNext2 = styler.SafeGetCharAt(i + 2);
261
if (chNext2 == '{') {
263
} else if (chNext2 == '}') {
268
if (foldDirective && (style == SCE_POV_DIRECTIVE)) {
271
while ((j<endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) {
276
if (style == SCE_POV_OPERATOR) {
279
} else if (ch == '}') {
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);
293
levelPrev = levelCurrent;
296
if (!isspacechar(ch))
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);
304
static const char * const povWordLists[] = {
305
"Language directives",
306
"Objects & CSG & Appearance",
307
"Types & Modifiers & Items",
308
"Predefined Identifiers",
309
"Predefined Functions",
316
LexerModule lmPOV(SCLEX_POV, ColourisePovDoc, "pov", FoldPovDoc, povWordLists);