]> git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/LexRebol.cxx
docopydocs is not needed for this script.
[wxWidgets.git] / src / stc / scintilla / src / LexRebol.cxx
1 // Scintilla source code edit control
2 /** @file LexRebol.cxx
3 ** Lexer for REBOL.
4 ** Written by Pascal Hurni, inspired from LexLua by Paul Winwood & Marcos E. Wurzius & Philippe Lhoste
5 **
6 ** History:
7 ** 2005-04-07 First release.
8 ** 2005-04-10 Closing parens and brackets go now in default style
9 ** String and comment nesting should be more safe
10 **/
11 // Copyright 2005 by Pascal Hurni <pascal_hurni@fastmail.fm>
12 // The License.txt file describes the conditions under which this software may be distributed.
13
14 #include <stdlib.h>
15 #include <string.h>
16 #include <ctype.h>
17 #include <stdio.h>
18 #include <stdarg.h>
19
20 #include "Platform.h"
21
22 #include "PropSet.h"
23 #include "Accessor.h"
24 #include "KeyWords.h"
25 #include "Scintilla.h"
26 #include "SciLexer.h"
27 #include "StyleContext.h"
28
29
30 static inline bool IsAWordChar(const int ch) {
31 return (isalnum(ch) || ch == '?' || ch == '!' || ch == '.' || ch == '\'' || ch == '+' || ch == '-' || ch == '*' || ch == '&' || ch == '|' || ch == '=' || ch == '_' || ch == '~');
32 }
33
34 static inline bool IsAWordStart(const int ch, const int ch2) {
35 return ((ch == '+' || ch == '-' || ch == '.') && !isdigit(ch2)) ||
36 (isalpha(ch) || ch == '?' || ch == '!' || ch == '\'' || ch == '*' || ch == '&' || ch == '|' || ch == '=' || ch == '_' || ch == '~');
37 }
38
39 static inline bool IsAnOperator(const int ch, const int ch2, const int ch3) {
40 // One char operators
41 if (IsASpaceOrTab(ch2)) {
42 return ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '<' || ch == '>' || ch == '=' || ch == '?';
43 }
44
45 // Two char operators
46 if (IsASpaceOrTab(ch3)) {
47 return (ch == '*' && ch2 == '*') ||
48 (ch == '/' && ch2 == '/') ||
49 (ch == '<' && (ch2 == '=' || ch2 == '>')) ||
50 (ch == '>' && ch2 == '=') ||
51 (ch == '=' && (ch2 == '=' || ch2 == '?')) ||
52 (ch == '?' && ch2 == '?');
53 }
54
55 return false;
56 }
57
58 static inline bool IsBinaryStart(const int ch, const int ch2, const int ch3, const int ch4) {
59 return (ch == '#' && ch2 == '{') ||
60 (IsADigit(ch) && ch2 == '#' && ch3 == '{' ) ||
61 (IsADigit(ch) && IsADigit(ch2) && ch3 == '#' && ch4 == '{' );
62 }
63
64
65 static void ColouriseRebolDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], Accessor &styler) {
66
67 WordList &keywords = *keywordlists[0];
68 WordList &keywords2 = *keywordlists[1];
69 WordList &keywords3 = *keywordlists[2];
70 WordList &keywords4 = *keywordlists[3];
71 WordList &keywords5 = *keywordlists[4];
72 WordList &keywords6 = *keywordlists[5];
73 WordList &keywords7 = *keywordlists[6];
74 WordList &keywords8 = *keywordlists[7];
75
76 int currentLine = styler.GetLine(startPos);
77 // Initialize the braced string {.. { ... } ..} nesting level, if we are inside such a string.
78 int stringLevel = 0;
79 if (initStyle == SCE_REBOL_BRACEDSTRING || initStyle == SCE_REBOL_COMMENTBLOCK) {
80 stringLevel = styler.GetLineState(currentLine - 1);
81 }
82
83 bool blockComment = initStyle == SCE_REBOL_COMMENTBLOCK;
84 int dotCount = 0;
85
86 // Do not leak onto next line
87 if (initStyle == SCE_REBOL_COMMENTLINE) {
88 initStyle = SCE_REBOL_DEFAULT;
89 }
90
91 StyleContext sc(startPos, length, initStyle, styler);
92 if (startPos == 0) {
93 sc.SetState(SCE_REBOL_PREFACE);
94 }
95 for (; sc.More(); sc.Forward()) {
96
97 //--- What to do at line end ?
98 if (sc.atLineEnd) {
99 // Can be either inside a {} string or simply at eol
100 if (sc.state != SCE_REBOL_BRACEDSTRING && sc.state != SCE_REBOL_COMMENTBLOCK &&
101 sc.state != SCE_REBOL_BINARY && sc.state != SCE_REBOL_PREFACE)
102 sc.SetState(SCE_REBOL_DEFAULT);
103
104 // Update the line state, so it can be seen by next line
105 currentLine = styler.GetLine(sc.currentPos);
106 switch (sc.state) {
107 case SCE_REBOL_BRACEDSTRING:
108 case SCE_REBOL_COMMENTBLOCK:
109 // Inside a braced string, we set the line state
110 styler.SetLineState(currentLine, stringLevel);
111 break;
112 default:
113 // Reset the line state
114 styler.SetLineState(currentLine, 0);
115 break;
116 }
117
118 // continue with next char
119 continue;
120 }
121
122 //--- What to do on white-space ?
123 if (IsASpaceOrTab(sc.ch))
124 {
125 // Return to default if any of these states
126 if (sc.state == SCE_REBOL_OPERATOR || sc.state == SCE_REBOL_CHARACTER ||
127 sc.state == SCE_REBOL_NUMBER || sc.state == SCE_REBOL_PAIR ||
128 sc.state == SCE_REBOL_TUPLE || sc.state == SCE_REBOL_FILE ||
129 sc.state == SCE_REBOL_DATE || sc.state == SCE_REBOL_TIME ||
130 sc.state == SCE_REBOL_MONEY || sc.state == SCE_REBOL_ISSUE ||
131 sc.state == SCE_REBOL_URL || sc.state == SCE_REBOL_EMAIL) {
132 sc.SetState(SCE_REBOL_DEFAULT);
133 }
134 }
135
136 //--- Specialize state ?
137 // URL, Email look like identifier
138 if (sc.state == SCE_REBOL_IDENTIFIER)
139 {
140 if (sc.ch == ':' && !IsASpace(sc.chNext)) {
141 sc.ChangeState(SCE_REBOL_URL);
142 } else if (sc.ch == '@') {
143 sc.ChangeState(SCE_REBOL_EMAIL);
144 } else if (sc.ch == '$') {
145 sc.ChangeState(SCE_REBOL_MONEY);
146 }
147 }
148 // Words look like identifiers
149 if (sc.state == SCE_REBOL_IDENTIFIER || (sc.state >= SCE_REBOL_WORD && sc.state <= SCE_REBOL_WORD8)) {
150 // Keywords ?
151 if (!IsAWordChar(sc.ch) || sc.Match('/')) {
152 char s[100];
153 sc.GetCurrentLowered(s, sizeof(s));
154 blockComment = strcmp(s, "comment") == 0;
155 if (keywords8.InList(s)) {
156 sc.ChangeState(SCE_REBOL_WORD8);
157 } else if (keywords7.InList(s)) {
158 sc.ChangeState(SCE_REBOL_WORD7);
159 } else if (keywords6.InList(s)) {
160 sc.ChangeState(SCE_REBOL_WORD6);
161 } else if (keywords5.InList(s)) {
162 sc.ChangeState(SCE_REBOL_WORD5);
163 } else if (keywords4.InList(s)) {
164 sc.ChangeState(SCE_REBOL_WORD4);
165 } else if (keywords3.InList(s)) {
166 sc.ChangeState(SCE_REBOL_WORD3);
167 } else if (keywords2.InList(s)) {
168 sc.ChangeState(SCE_REBOL_WORD2);
169 } else if (keywords.InList(s)) {
170 sc.ChangeState(SCE_REBOL_WORD);
171 }
172 // Keep same style if there are refinements
173 if (!sc.Match('/')) {
174 sc.SetState(SCE_REBOL_DEFAULT);
175 }
176 }
177 // special numbers
178 } else if (sc.state == SCE_REBOL_NUMBER) {
179 switch (sc.ch) {
180 case 'x': sc.ChangeState(SCE_REBOL_PAIR);
181 break;
182 case ':': sc.ChangeState(SCE_REBOL_TIME);
183 break;
184 case '-':
185 case '/': sc.ChangeState(SCE_REBOL_DATE);
186 break;
187 case '.': if (++dotCount >= 2) sc.ChangeState(SCE_REBOL_TUPLE);
188 break;
189 }
190 }
191
192 //--- Determine if the current state should terminate
193 if (sc.state == SCE_REBOL_QUOTEDSTRING || sc.state == SCE_REBOL_CHARACTER) {
194 if (sc.ch == '^' && sc.chNext == '\"') {
195 sc.Forward();
196 } else if (sc.ch == '\"') {
197 sc.ForwardSetState(SCE_REBOL_DEFAULT);
198 }
199 } else if (sc.state == SCE_REBOL_BRACEDSTRING || sc.state == SCE_REBOL_COMMENTBLOCK) {
200 if (sc.ch == '}') {
201 if (--stringLevel == 0) {
202 sc.ForwardSetState(SCE_REBOL_DEFAULT);
203 }
204 } else if (sc.ch == '{') {
205 stringLevel++;
206 }
207 } else if (sc.state == SCE_REBOL_BINARY) {
208 if (sc.ch == '}') {
209 sc.ForwardSetState(SCE_REBOL_DEFAULT);
210 }
211 } else if (sc.state == SCE_REBOL_TAG) {
212 if (sc.ch == '>') {
213 sc.ForwardSetState(SCE_REBOL_DEFAULT);
214 }
215 } else if (sc.state == SCE_REBOL_PREFACE) {
216 if (sc.MatchIgnoreCase("rebol"))
217 {
218 int i;
219 for (i=5; IsASpaceOrTab(styler.SafeGetCharAt(sc.currentPos+i, 0)); i++);
220 if (sc.GetRelative(i) == '[')
221 sc.SetState(SCE_REBOL_DEFAULT);
222 }
223 }
224
225 //--- Parens and bracket changes to default style when the current is a number
226 if (sc.state == SCE_REBOL_NUMBER || sc.state == SCE_REBOL_PAIR || sc.state == SCE_REBOL_TUPLE ||
227 sc.state == SCE_REBOL_MONEY || sc.state == SCE_REBOL_ISSUE || sc.state == SCE_REBOL_EMAIL ||
228 sc.state == SCE_REBOL_URL || sc.state == SCE_REBOL_DATE || sc.state == SCE_REBOL_TIME) {
229 if (sc.ch == '(' || sc.ch == '[' || sc.ch == ')' || sc.ch == ']') {
230 sc.SetState(SCE_REBOL_DEFAULT);
231 }
232 }
233
234 //--- Determine if a new state should be entered.
235 if (sc.state == SCE_REBOL_DEFAULT) {
236 if (IsAnOperator(sc.ch, sc.chNext, sc.GetRelative(2))) {
237 sc.SetState(SCE_REBOL_OPERATOR);
238 } else if (IsBinaryStart(sc.ch, sc.chNext, sc.GetRelative(2), sc.GetRelative(3))) {
239 sc.SetState(SCE_REBOL_BINARY);
240 } else if (IsAWordStart(sc.ch, sc.chNext)) {
241 sc.SetState(SCE_REBOL_IDENTIFIER);
242 } else if (IsADigit(sc.ch) || sc.ch == '+' || sc.ch == '-' || /*Decimal*/ sc.ch == '.' || sc.ch == ',') {
243 dotCount = 0;
244 sc.SetState(SCE_REBOL_NUMBER);
245 } else if (sc.ch == '\"') {
246 sc.SetState(SCE_REBOL_QUOTEDSTRING);
247 } else if (sc.ch == '{') {
248 sc.SetState(blockComment ? SCE_REBOL_COMMENTBLOCK : SCE_REBOL_BRACEDSTRING);
249 ++stringLevel;
250 } else if (sc.ch == ';') {
251 sc.SetState(SCE_REBOL_COMMENTLINE);
252 } else if (sc.ch == '$') {
253 sc.SetState(SCE_REBOL_MONEY);
254 } else if (sc.ch == '%') {
255 sc.SetState(SCE_REBOL_FILE);
256 } else if (sc.ch == '<') {
257 sc.SetState(SCE_REBOL_TAG);
258 } else if (sc.ch == '#' && sc.chNext == '"') {
259 sc.SetState(SCE_REBOL_CHARACTER);
260 sc.Forward();
261 } else if (sc.ch == '#' && sc.chNext != '"' && sc.chNext != '{' ) {
262 sc.SetState(SCE_REBOL_ISSUE);
263 }
264 }
265 }
266 sc.Complete();
267 }
268
269
270 static void FoldRebolDoc(unsigned int startPos, int length, int /* initStyle */, WordList *[],
271 Accessor &styler) {
272 unsigned int lengthDoc = startPos + length;
273 int visibleChars = 0;
274 int lineCurrent = styler.GetLine(startPos);
275 int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
276 int levelCurrent = levelPrev;
277 char chNext = styler[startPos];
278 int styleNext = styler.StyleAt(startPos);
279 for (unsigned int i = startPos; i < lengthDoc; i++) {
280 char ch = chNext;
281 chNext = styler.SafeGetCharAt(i + 1);
282 int style = styleNext;
283 styleNext = styler.StyleAt(i + 1);
284 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
285 if (style == SCE_REBOL_DEFAULT) {
286 if (ch == '[') {
287 levelCurrent++;
288 } else if (ch == ']') {
289 levelCurrent--;
290 }
291 }
292 if (atEOL) {
293 int lev = levelPrev;
294 if (visibleChars == 0)
295 lev |= SC_FOLDLEVELWHITEFLAG;
296 if ((levelCurrent > levelPrev) && (visibleChars > 0))
297 lev |= SC_FOLDLEVELHEADERFLAG;
298 if (lev != styler.LevelAt(lineCurrent)) {
299 styler.SetLevel(lineCurrent, lev);
300 }
301 lineCurrent++;
302 levelPrev = levelCurrent;
303 visibleChars = 0;
304 }
305 if (!isspacechar(ch))
306 visibleChars++;
307 }
308 // Fill in the real level of the next line, keeping the current flags as they will be filled in later
309 int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
310 styler.SetLevel(lineCurrent, levelPrev | flagsNext);
311 }
312
313 static const char * const rebolWordListDesc[] = {
314 "Keywords",
315 0
316 };
317
318 LexerModule lmREBOL(SCLEX_REBOL, ColouriseRebolDoc, "rebol", FoldRebolDoc, rebolWordListDesc);
319