]>
Commit | Line | Data |
---|---|---|
65ec6247 RD |
1 | // Scintilla source code edit control |
2 | /** @file LexPerl.cxx | |
3 | ** Lexer for subset of Perl. | |
4 | **/ | |
5 | // Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org> | |
f6bcfd97 BP |
6 | // The License.txt file describes the conditions under which this software may be distributed. |
7 | ||
65ec6247 RD |
8 | #include <stdlib.h> |
9 | #include <string.h> | |
10 | #include <ctype.h> | |
11 | #include <stdio.h> | |
12 | #include <stdarg.h> | |
f6bcfd97 BP |
13 | |
14 | #include "Platform.h" | |
15 | ||
16 | #include "PropSet.h" | |
17 | #include "Accessor.h" | |
18 | #include "KeyWords.h" | |
19 | #include "Scintilla.h" | |
20 | #include "SciLexer.h" | |
21 | ||
65ec6247 RD |
22 | static inline bool isEOLChar(char ch) { |
23 | return (ch == '\r') || (ch == '\n'); | |
24 | } | |
25 | ||
26 | static bool isSingleCharOp(char ch) { | |
27 | char strCharSet[2]; | |
28 | strCharSet[0] = ch; | |
29 | strCharSet[1] = '\0'; | |
30 | return (NULL != strstr("rwxoRWXOezsfdlpSbctugkTBMAC", strCharSet)); | |
31 | } | |
32 | ||
33 | static inline bool isPerlOperator(char ch) { | |
f6bcfd97 BP |
34 | if (isalnum(ch)) |
35 | return false; | |
36 | // '.' left out as it is used to make up numbers | |
37 | if (ch == '%' || ch == '^' || ch == '&' || ch == '*' || ch == '\\' || | |
38 | ch == '(' || ch == ')' || ch == '-' || ch == '+' || | |
39 | ch == '=' || ch == '|' || ch == '{' || ch == '}' || | |
40 | ch == '[' || ch == ']' || ch == ':' || ch == ';' || | |
41 | ch == '<' || ch == '>' || ch == ',' || ch == '/' || | |
42 | ch == '?' || ch == '!' || ch == '.' || ch == '~') | |
43 | return true; | |
44 | return false; | |
45 | } | |
46 | ||
47 | static int classifyWordPerl(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler) { | |
48 | char s[100]; | |
49 | bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.'); | |
50 | for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { | |
51 | s[i] = styler[start + i]; | |
52 | s[i + 1] = '\0'; | |
53 | } | |
54 | char chAttr = SCE_PL_IDENTIFIER; | |
55 | if (wordIsNumber) | |
56 | chAttr = SCE_PL_NUMBER; | |
57 | else { | |
58 | if (keywords.InList(s)) | |
59 | chAttr = SCE_PL_WORD; | |
60 | } | |
61 | styler.ColourTo(end, chAttr); | |
62 | return chAttr; | |
63 | } | |
64 | ||
65ec6247 | 65 | static inline bool isEndVar(char ch) { |
f6bcfd97 BP |
66 | return !isalnum(ch) && ch != '#' && ch != '$' && |
67 | ch != '_' && ch != '\''; | |
68 | } | |
69 | ||
70 | static bool isMatch(Accessor &styler, int lengthDoc, int pos, const char *val) { | |
71 | if ((pos + static_cast<int>(strlen(val))) >= lengthDoc) { | |
72 | return false; | |
73 | } | |
74 | while (*val) { | |
75 | if (*val != styler[pos++]) { | |
76 | return false; | |
77 | } | |
78 | val++; | |
79 | } | |
80 | return true; | |
81 | } | |
82 | ||
83 | static char opposite(char ch) { | |
84 | if (ch == '(') | |
85 | return ')'; | |
86 | if (ch == '[') | |
87 | return ']'; | |
88 | if (ch == '{') | |
89 | return '}'; | |
90 | if (ch == '<') | |
91 | return '>'; | |
92 | return ch; | |
93 | } | |
94 | ||
95 | static void ColourisePerlDoc(unsigned int startPos, int length, int initStyle, | |
96 | WordList *keywordlists[], Accessor &styler) { | |
97 | ||
98 | // Lexer for perl often has to backtrack to start of current style to determine | |
99 | // which characters are being used as quotes, how deeply nested is the | |
100 | // start position and what the termination string is for here documents | |
65ec6247 | 101 | |
f6bcfd97 | 102 | WordList &keywords = *keywordlists[0]; |
65ec6247 RD |
103 | |
104 | class HereDocCls { | |
105 | public: | |
106 | int State; // 0: '<<' encountered | |
107 | // 1: collect the delimiter | |
108 | // 2: here doc text (lines after the delimiter) | |
109 | char Quote; // the char after '<<' | |
110 | bool Quoted; // true if Quote in ('\'','"','`') | |
111 | int DelimiterLength; // strlen(Delimiter) | |
112 | char Delimiter[256]; // the Delimiter, 256: sizeof PL_tokenbuf | |
113 | HereDocCls() { | |
114 | State = 0; | |
115 | DelimiterLength = 0; | |
116 | Delimiter[0] = '\0'; | |
117 | } | |
118 | }; | |
119 | HereDocCls HereDoc; // TODO: FIFO for stacked here-docs | |
120 | ||
121 | class QuoteCls { | |
122 | public: | |
123 | int Rep; | |
124 | int Count; | |
125 | char Up; | |
126 | char Down; | |
127 | QuoteCls() { | |
128 | this->New(1); | |
129 | } | |
130 | void New(int r) { | |
131 | Rep = r; | |
132 | Count = 0; | |
133 | Up = '\0'; | |
134 | Down = '\0'; | |
135 | } | |
136 | void Open(char u) { | |
137 | Count++; | |
138 | Up = u; | |
139 | Down = opposite(Up); | |
140 | } | |
141 | }; | |
142 | QuoteCls Quote; | |
143 | ||
f6bcfd97 | 144 | char sooked[100]; |
f6bcfd97 BP |
145 | int sookedpos = 0; |
146 | bool preferRE = true; | |
147 | sooked[sookedpos] = '\0'; | |
148 | int state = initStyle; | |
65ec6247 RD |
149 | unsigned int lengthDoc = startPos + length; |
150 | ||
f6bcfd97 | 151 | // If in a long distance lexical state, seek to the beginning to find quote characters |
65ec6247 RD |
152 | if (state == SCE_PL_HERE_Q || state == SCE_PL_HERE_QQ || state == SCE_PL_HERE_QX) { |
153 | while ((startPos > 1) && (styler.StyleAt(startPos) != SCE_PL_HERE_DELIM)) { | |
154 | startPos--; | |
155 | } | |
156 | startPos = styler.LineStart(styler.GetLine(startPos)); | |
157 | state = styler.StyleAt(startPos - 1); | |
158 | } | |
159 | if ( state == SCE_PL_STRING_Q | |
160 | || state == SCE_PL_STRING_QQ | |
161 | || state == SCE_PL_STRING_QX | |
162 | || state == SCE_PL_STRING_QR | |
163 | || state == SCE_PL_STRING_QW | |
164 | || state == SCE_PL_REGEX | |
165 | || state == SCE_PL_REGSUBST | |
166 | ) { | |
f6bcfd97 BP |
167 | while ((startPos > 1) && (styler.StyleAt(startPos - 1) == state)) { |
168 | startPos--; | |
169 | } | |
170 | state = SCE_PL_DEFAULT; | |
171 | } | |
65ec6247 | 172 | |
f6bcfd97 | 173 | styler.StartAt(startPos); |
d134f170 | 174 | char chPrev = styler.SafeGetCharAt(startPos - 1); |
65ec6247 RD |
175 | if (startPos == 0) |
176 | chPrev = '\n'; | |
f6bcfd97 BP |
177 | char chNext = styler[startPos]; |
178 | styler.StartSegment(startPos); | |
65ec6247 RD |
179 | |
180 | for (unsigned int i = startPos; i < lengthDoc; i++) { | |
f6bcfd97 BP |
181 | char ch = chNext; |
182 | chNext = styler.SafeGetCharAt(i + 1); | |
183 | char chNext2 = styler.SafeGetCharAt(i + 2); | |
184 | ||
185 | if (styler.IsLeadByte(ch)) { | |
186 | chNext = styler.SafeGetCharAt(i + 2); | |
187 | chPrev = ' '; | |
188 | i += 1; | |
189 | continue; | |
190 | } | |
65ec6247 RD |
191 | if ((chPrev == '\r' && ch == '\n')) { // skip on DOS/Windows |
192 | chPrev = ch; | |
193 | continue; | |
194 | } | |
195 | ||
196 | if (HereDoc.State == 1 && isEOLChar(ch)) { | |
197 | // Begin of here-doc (the line after the here-doc delimiter): | |
198 | HereDoc.State = 2; | |
199 | styler.ColourTo(i - 1, state); | |
200 | if (HereDoc.Quoted) { | |
201 | if (state == SCE_PL_HERE_DELIM) { | |
202 | // Missing quote at end of string! We are stricter than perl. | |
203 | state = SCE_PL_ERROR; | |
204 | } else { | |
205 | switch (HereDoc.Quote) { | |
206 | case '\'': | |
207 | state = SCE_PL_HERE_Q ; | |
208 | break; | |
209 | case '"': | |
210 | state = SCE_PL_HERE_QQ; | |
211 | break; | |
212 | case '`': | |
213 | state = SCE_PL_HERE_QX; | |
214 | break; | |
215 | } | |
216 | } | |
217 | } else { | |
218 | switch (HereDoc.Quote) { | |
219 | case '\\': | |
220 | state = SCE_PL_HERE_Q ; | |
221 | break; | |
222 | default : | |
223 | state = SCE_PL_HERE_QQ; | |
224 | } | |
225 | } | |
226 | } | |
f6bcfd97 BP |
227 | |
228 | if (state == SCE_PL_DEFAULT) { | |
229 | if (iswordstart(ch)) { | |
230 | styler.ColourTo(i - 1, state); | |
231 | if (ch == 's' && !isalnum(chNext)) { | |
232 | state = SCE_PL_REGSUBST; | |
65ec6247 | 233 | Quote.New(2); |
f6bcfd97 BP |
234 | } else if (ch == 'm' && !isalnum(chNext)) { |
235 | state = SCE_PL_REGEX; | |
65ec6247 RD |
236 | Quote.New(1); |
237 | } else if (ch == 'q' && !isalnum(chNext)) { | |
238 | state = SCE_PL_STRING_Q; | |
239 | Quote.New(1); | |
240 | } else if (ch == 'y' && !isalnum(chNext)) { | |
241 | state = SCE_PL_REGSUBST; | |
242 | Quote.New(2); | |
f6bcfd97 BP |
243 | } else if (ch == 't' && chNext == 'r' && !isalnum(chNext2)) { |
244 | state = SCE_PL_REGSUBST; | |
65ec6247 | 245 | Quote.New(2); |
f6bcfd97 BP |
246 | i++; |
247 | chNext = chNext2; | |
248 | } else if (ch == 'q' && (chNext == 'q' || chNext == 'r' || chNext == 'w' || chNext == 'x') && !isalnum(chNext2)) { | |
65ec6247 RD |
249 | if (chNext == 'q') state = SCE_PL_STRING_QQ; |
250 | else if (chNext == 'x') state = SCE_PL_STRING_QX; | |
251 | else if (chNext == 'r') state = SCE_PL_STRING_QR; | |
252 | else if (chNext == 'w') state = SCE_PL_STRING_QW; | |
f6bcfd97 BP |
253 | i++; |
254 | chNext = chNext2; | |
65ec6247 | 255 | Quote.New(1); |
f6bcfd97 BP |
256 | } else { |
257 | state = SCE_PL_WORD; | |
258 | preferRE = false; | |
65ec6247 RD |
259 | if ((!iswordchar(chNext) && chNext != '\'') |
260 | || (chNext == '.' && chNext2 == '.')) { | |
261 | // We need that if length of word == 1! | |
262 | // This test is copied from the SCE_PL_WORD handler. | |
263 | classifyWordPerl(styler.GetStartSegment(), i, keywords, styler); | |
264 | state = SCE_PL_DEFAULT; | |
265 | } | |
f6bcfd97 BP |
266 | } |
267 | } else if (ch == '#') { | |
268 | styler.ColourTo(i - 1, state); | |
269 | state = SCE_PL_COMMENTLINE; | |
270 | } else if (ch == '\"') { | |
271 | styler.ColourTo(i - 1, state); | |
272 | state = SCE_PL_STRING; | |
65ec6247 RD |
273 | Quote.New(1); |
274 | Quote.Open(ch); | |
f6bcfd97 BP |
275 | } else if (ch == '\'') { |
276 | if (chPrev == '&') { | |
277 | // Archaic call | |
278 | styler.ColourTo(i, state); | |
279 | } else { | |
280 | styler.ColourTo(i - 1, state); | |
281 | state = SCE_PL_CHARACTER; | |
65ec6247 RD |
282 | Quote.New(1); |
283 | Quote.Open(ch); | |
f6bcfd97 BP |
284 | } |
285 | } else if (ch == '`') { | |
286 | styler.ColourTo(i - 1, state); | |
287 | state = SCE_PL_BACKTICKS; | |
65ec6247 RD |
288 | Quote.New(1); |
289 | Quote.Open(ch); | |
f6bcfd97 BP |
290 | } else if (ch == '$') { |
291 | preferRE = false; | |
292 | styler.ColourTo(i - 1, state); | |
65ec6247 | 293 | if ((chNext == '{') || isspacechar(chNext)) { |
f6bcfd97 | 294 | styler.ColourTo(i, SCE_PL_SCALAR); |
f6bcfd97 | 295 | } else { |
65ec6247 RD |
296 | state = SCE_PL_SCALAR; |
297 | i++; | |
298 | ch = chNext; | |
299 | chNext = chNext2; | |
f6bcfd97 BP |
300 | } |
301 | } else if (ch == '@') { | |
302 | preferRE = false; | |
303 | styler.ColourTo(i - 1, state); | |
304 | if (isalpha(chNext) || chNext == '#' || chNext == '$' || chNext == '_') { | |
305 | state = SCE_PL_ARRAY; | |
306 | } else if (chNext != '{' && chNext != '[') { | |
307 | styler.ColourTo(i, SCE_PL_ARRAY); | |
308 | i++; | |
309 | ch = ' '; | |
310 | } else { | |
311 | styler.ColourTo(i, SCE_PL_ARRAY); | |
312 | } | |
313 | } else if (ch == '%') { | |
314 | preferRE = false; | |
315 | styler.ColourTo(i - 1, state); | |
316 | if (isalpha(chNext) || chNext == '#' || chNext == '$' || chNext == '_') { | |
317 | state = SCE_PL_HASH; | |
65ec6247 | 318 | } else if (chNext == '{') { |
f6bcfd97 | 319 | styler.ColourTo(i, SCE_PL_HASH); |
f6bcfd97 | 320 | } else { |
65ec6247 | 321 | styler.ColourTo(i, SCE_PL_OPERATOR); |
f6bcfd97 BP |
322 | } |
323 | } else if (ch == '*') { | |
324 | styler.ColourTo(i - 1, state); | |
325 | state = SCE_PL_SYMBOLTABLE; | |
326 | } else if (ch == '/' && preferRE) { | |
327 | styler.ColourTo(i - 1, state); | |
328 | state = SCE_PL_REGEX; | |
65ec6247 RD |
329 | Quote.New(1); |
330 | Quote.Open(ch); | |
f6bcfd97 BP |
331 | } else if (ch == '<' && chNext == '<') { |
332 | styler.ColourTo(i - 1, state); | |
65ec6247 RD |
333 | state = SCE_PL_HERE_DELIM; |
334 | HereDoc.State = 0; | |
335 | } else if (ch == '=' | |
336 | && isalpha(chNext) | |
337 | && (isEOLChar(chPrev))) { | |
f6bcfd97 BP |
338 | styler.ColourTo(i - 1, state); |
339 | state = SCE_PL_POD; | |
f6bcfd97 BP |
340 | sookedpos = 0; |
341 | sooked[sookedpos] = '\0'; | |
65ec6247 RD |
342 | } else if (ch == '-' |
343 | && isSingleCharOp(chNext) | |
344 | && !isalnum((chNext2 = styler.SafeGetCharAt(i+2)))) { | |
345 | styler.ColourTo(i - 1, state); | |
346 | styler.ColourTo(i + 1, SCE_PL_WORD); | |
347 | state = SCE_PL_DEFAULT; | |
348 | preferRE = false; | |
349 | i += 2; | |
350 | ch = chNext2; | |
351 | chNext = chNext2 = styler.SafeGetCharAt(i + 1); | |
f6bcfd97 BP |
352 | } else if (isPerlOperator(ch)) { |
353 | if (ch == ')' || ch == ']') | |
354 | preferRE = false; | |
355 | else | |
356 | preferRE = true; | |
357 | styler.ColourTo(i - 1, state); | |
358 | styler.ColourTo(i, SCE_PL_OPERATOR); | |
359 | } | |
360 | } else if (state == SCE_PL_WORD) { | |
65ec6247 RD |
361 | if ((!iswordchar(chNext) && chNext != '\'') |
362 | || (chNext == '.' && chNext2 == '.')) { | |
363 | // ".." is always an operator if preceded by a SCE_PL_WORD. | |
364 | // Archaic Perl has quotes inside names | |
f6bcfd97 BP |
365 | if (isMatch(styler, lengthDoc, styler.GetStartSegment(), "__DATA__")) { |
366 | styler.ColourTo(i, SCE_PL_DATASECTION); | |
367 | state = SCE_PL_DATASECTION; | |
368 | } else if (isMatch(styler, lengthDoc, styler.GetStartSegment(), "__END__")) { | |
369 | styler.ColourTo(i, SCE_PL_DATASECTION); | |
370 | state = SCE_PL_DATASECTION; | |
371 | } else { | |
65ec6247 | 372 | if (classifyWordPerl(styler.GetStartSegment(), i, keywords, styler) == SCE_PL_WORD) |
f6bcfd97 BP |
373 | preferRE = true; |
374 | state = SCE_PL_DEFAULT; | |
65ec6247 | 375 | ch = ' '; |
f6bcfd97 BP |
376 | } |
377 | } | |
378 | } else { | |
379 | if (state == SCE_PL_COMMENTLINE) { | |
65ec6247 | 380 | if (isEOLChar(ch)) { |
f6bcfd97 BP |
381 | styler.ColourTo(i - 1, state); |
382 | state = SCE_PL_DEFAULT; | |
383 | } | |
65ec6247 RD |
384 | } else if (state == SCE_PL_HERE_DELIM) { |
385 | // | |
386 | // From perldata.pod: | |
387 | // ------------------ | |
388 | // A line-oriented form of quoting is based on the shell ``here-doc'' | |
389 | // syntax. | |
390 | // Following a << you specify a string to terminate the quoted material, | |
391 | // and all lines following the current line down to the terminating | |
392 | // string are the value of the item. | |
393 | // The terminating string may be either an identifier (a word), | |
394 | // or some quoted text. | |
395 | // If quoted, the type of quotes you use determines the treatment of | |
396 | // the text, just as in regular quoting. | |
397 | // An unquoted identifier works like double quotes. | |
398 | // There must be no space between the << and the identifier. | |
399 | // (If you put a space it will be treated as a null identifier, | |
400 | // which is valid, and matches the first empty line.) | |
401 | // The terminating string must appear by itself (unquoted and with no | |
402 | // surrounding whitespace) on the terminating line. | |
403 | // | |
404 | if (HereDoc.State == 0) { // '<<' encountered | |
405 | HereDoc.State = 1; | |
406 | HereDoc.Quote = chNext; | |
407 | HereDoc.Quoted = false; | |
408 | HereDoc.DelimiterLength = 0; | |
409 | HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0'; | |
410 | if (chNext == '\'' || chNext == '"' || chNext == '`') { // a quoted here-doc delimiter | |
f6bcfd97 BP |
411 | i++; |
412 | ch = chNext; | |
65ec6247 RD |
413 | chNext = chNext2; |
414 | HereDoc.Quoted = true; | |
415 | } else if (chNext == '\\') { // ref? | |
f6bcfd97 BP |
416 | i++; |
417 | ch = chNext; | |
65ec6247 RD |
418 | chNext = chNext2; |
419 | } else if (isalnum(chNext) || chNext == '_') { // an unquoted here-doc delimiter | |
420 | } | |
421 | else if (isspacechar(chNext)) { // deprecated here-doc delimiter || TODO: left shift operator | |
422 | } | |
423 | else { // TODO: ??? | |
424 | } | |
425 | ||
426 | } else if (HereDoc.State == 1) { // collect the delimiter | |
427 | if (HereDoc.Quoted) { // a quoted here-doc delimiter | |
428 | if (ch == HereDoc.Quote) { // closing quote => end of delimiter | |
429 | styler.ColourTo(i, state); | |
430 | state = SCE_PL_DEFAULT; | |
431 | i++; | |
432 | ch = chNext; | |
433 | chNext = chNext2; | |
434 | } else { | |
435 | if (ch == '\\' && chNext == HereDoc.Quote) { // escaped quote | |
436 | i++; | |
437 | ch = chNext; | |
438 | chNext = chNext2; | |
439 | } | |
440 | HereDoc.Delimiter[HereDoc.DelimiterLength++] = ch; | |
441 | HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0'; | |
442 | } | |
443 | } else { // an unquoted here-doc delimiter | |
444 | if (isalnum(ch) || ch == '_') { | |
445 | HereDoc.Delimiter[HereDoc.DelimiterLength++] = ch; | |
446 | HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0'; | |
447 | } else { | |
448 | styler.ColourTo(i - 1, state); | |
449 | state = SCE_PL_DEFAULT; | |
450 | } | |
451 | } | |
452 | if (HereDoc.DelimiterLength >= static_cast<int>(sizeof(HereDoc.Delimiter)) - 1) { | |
453 | styler.ColourTo(i - 1, state); | |
454 | state = SCE_PL_ERROR; | |
f6bcfd97 | 455 | } |
f6bcfd97 | 456 | } |
65ec6247 RD |
457 | } else if (HereDoc.State == 2) { |
458 | // state == SCE_PL_HERE_Q || state == SCE_PL_HERE_QQ || state == SCE_PL_HERE_QX | |
459 | if (isEOLChar(chPrev) && isMatch(styler, lengthDoc, i, HereDoc.Delimiter)) { | |
460 | i += HereDoc.DelimiterLength; | |
461 | chNext = styler.SafeGetCharAt(i); | |
462 | if (isEOLChar(chNext)) { | |
463 | styler.ColourTo(i - 1, state); | |
464 | state = SCE_PL_DEFAULT; | |
465 | HereDoc.State = 0; | |
466 | } | |
f6bcfd97 BP |
467 | ch = chNext; |
468 | chNext = styler.SafeGetCharAt(i + 1); | |
469 | } | |
470 | } else if (state == SCE_PL_POD) { | |
65ec6247 | 471 | if (ch == '=' && isEOLChar(chPrev)) { |
f6bcfd97 BP |
472 | if (isMatch(styler, lengthDoc, i, "=cut")) { |
473 | styler.ColourTo(i - 1 + 4, state); | |
474 | i += 4; | |
475 | state = SCE_PL_DEFAULT; | |
d134f170 RD |
476 | ch = styler.SafeGetCharAt(i); |
477 | chNext = styler.SafeGetCharAt(i + 1); | |
f6bcfd97 BP |
478 | } |
479 | } | |
480 | } else if (state == SCE_PL_SCALAR) { | |
481 | if (isEndVar(ch)) { | |
65ec6247 RD |
482 | if (i == (styler.GetStartSegment() + 1)) { |
483 | // Special variable: $(, $_ etc. | |
484 | styler.ColourTo(i, state); | |
485 | } else { | |
486 | styler.ColourTo(i - 1, state); | |
487 | } | |
f6bcfd97 BP |
488 | state = SCE_PL_DEFAULT; |
489 | } | |
490 | } else if (state == SCE_PL_ARRAY) { | |
491 | if (isEndVar(ch)) { | |
492 | styler.ColourTo(i - 1, state); | |
493 | state = SCE_PL_DEFAULT; | |
494 | } | |
495 | } else if (state == SCE_PL_HASH) { | |
496 | if (isEndVar(ch)) { | |
497 | styler.ColourTo(i - 1, state); | |
498 | state = SCE_PL_DEFAULT; | |
499 | } | |
500 | } else if (state == SCE_PL_SYMBOLTABLE) { | |
501 | if (isEndVar(ch)) { | |
502 | styler.ColourTo(i - 1, state); | |
503 | state = SCE_PL_DEFAULT; | |
504 | } | |
65ec6247 RD |
505 | } else if (state == SCE_PL_REGEX |
506 | || state == SCE_PL_STRING_QR | |
507 | ) { | |
508 | if (!Quote.Up && !isspacechar(ch)) { | |
509 | Quote.Open(ch); | |
510 | } else if (ch == '\\' && Quote.Up != '\\') { | |
511 | // SG: Is it save to skip *every* escaped char? | |
512 | i++; | |
513 | ch = chNext; | |
514 | chNext = styler.SafeGetCharAt(i + 1); | |
f6bcfd97 | 515 | } else { |
65ec6247 RD |
516 | if (ch == Quote.Down /*&& chPrev != '\\'*/) { |
517 | Quote.Count--; | |
518 | if (Quote.Count == 0) { | |
519 | Quote.Rep--; | |
520 | if (Quote.Up == Quote.Down) { | |
521 | Quote.Count++; | |
f6bcfd97 BP |
522 | } |
523 | } | |
524 | if (!isalpha(chNext)) { | |
65ec6247 | 525 | if (Quote.Rep <= 0) { |
f6bcfd97 BP |
526 | styler.ColourTo(i, state); |
527 | state = SCE_PL_DEFAULT; | |
528 | ch = ' '; | |
529 | } | |
530 | } | |
65ec6247 RD |
531 | } else if (ch == Quote.Up /*&& chPrev != '\\'*/) { |
532 | Quote.Count++; | |
f6bcfd97 | 533 | } else if (!isalpha(chNext)) { |
65ec6247 | 534 | if (Quote.Rep <= 0) { |
f6bcfd97 BP |
535 | styler.ColourTo(i, state); |
536 | state = SCE_PL_DEFAULT; | |
537 | ch = ' '; | |
538 | } | |
539 | } | |
540 | } | |
541 | } else if (state == SCE_PL_REGSUBST) { | |
65ec6247 RD |
542 | if (!Quote.Up && !isspacechar(ch)) { |
543 | Quote.Open(ch); | |
544 | } else if (ch == '\\' && Quote.Up != '\\') { | |
545 | // SG: Is it save to skip *every* escaped char? | |
546 | i++; | |
547 | ch = chNext; | |
548 | chNext = styler.SafeGetCharAt(i + 1); | |
f6bcfd97 | 549 | } else { |
65ec6247 | 550 | if (Quote.Count == 0 && Quote.Rep == 1) { |
d134f170 RD |
551 | /* We matched something like s(...) or tr{...} |
552 | * and are looking for the next matcher characters, | |
553 | * which could be either bracketed ({...}) or non-bracketed | |
554 | * (/.../). | |
555 | * | |
556 | * Number-signs are problematic. If they occur after | |
557 | * the close of the first part, treat them like | |
65ec6247 | 558 | * a Quote.Up char, even if they actually start comments. |
d134f170 RD |
559 | * |
560 | * If we find an alnum, we end the regsubst, and punt. | |
561 | * | |
562 | * Eric Promislow ericp@activestate.com Aug 9,2000 | |
563 | */ | |
65ec6247 | 564 | if (isspacechar(ch)) { |
d134f170 | 565 | // Keep going |
65ec6247 RD |
566 | } |
567 | else if (isalnum(ch)) { | |
d134f170 RD |
568 | styler.ColourTo(i, state); |
569 | state = SCE_PL_DEFAULT; | |
570 | ch = ' '; | |
571 | } else { | |
65ec6247 | 572 | Quote.Open(ch); |
d134f170 | 573 | } |
65ec6247 RD |
574 | } else if (ch == Quote.Down /*&& chPrev != '\\'*/) { |
575 | Quote.Count--; | |
576 | if (Quote.Count == 0) { | |
577 | Quote.Rep--; | |
f6bcfd97 BP |
578 | } |
579 | if (!isalpha(chNext)) { | |
65ec6247 | 580 | if (Quote.Rep <= 0) { |
f6bcfd97 BP |
581 | styler.ColourTo(i, state); |
582 | state = SCE_PL_DEFAULT; | |
583 | ch = ' '; | |
584 | } | |
585 | } | |
65ec6247 RD |
586 | if (Quote.Up == Quote.Down) { |
587 | Quote.Count++; | |
f6bcfd97 | 588 | } |
65ec6247 RD |
589 | } else if (ch == Quote.Up /*&& chPrev != '\\'*/) { |
590 | Quote.Count++; | |
f6bcfd97 | 591 | } else if (!isalpha(chNext)) { |
65ec6247 | 592 | if (Quote.Rep <= 0) { |
f6bcfd97 BP |
593 | styler.ColourTo(i, state); |
594 | state = SCE_PL_DEFAULT; | |
595 | ch = ' '; | |
596 | } | |
597 | } | |
598 | } | |
65ec6247 RD |
599 | } else if (state == SCE_PL_STRING_Q |
600 | || state == SCE_PL_STRING_QQ | |
601 | || state == SCE_PL_STRING_QX | |
602 | || state == SCE_PL_STRING_QW | |
603 | || state == SCE_PL_STRING | |
604 | || state == SCE_PL_CHARACTER | |
605 | || state == SCE_PL_BACKTICKS | |
606 | ) { | |
607 | if (!Quote.Down && !isspacechar(ch)) { | |
608 | Quote.Open(ch); | |
609 | } else if (ch == '\\' && Quote.Up != '\\') { | |
610 | i++; | |
611 | ch = chNext; | |
612 | chNext = styler.SafeGetCharAt(i + 1); | |
613 | } else if (ch == Quote.Down) { | |
614 | Quote.Count--; | |
615 | if (Quote.Count == 0) { | |
616 | Quote.Rep--; | |
617 | if (Quote.Rep <= 0) { | |
f6bcfd97 BP |
618 | styler.ColourTo(i, state); |
619 | state = SCE_PL_DEFAULT; | |
620 | ch = ' '; | |
621 | } | |
65ec6247 RD |
622 | if (Quote.Up == Quote.Down) { |
623 | Quote.Count++; | |
f6bcfd97 BP |
624 | } |
625 | } | |
65ec6247 RD |
626 | } else if (ch == Quote.Up) { |
627 | Quote.Count++; | |
f6bcfd97 BP |
628 | } |
629 | } | |
630 | ||
631 | if (state == SCE_PL_DEFAULT) { // One of the above succeeded | |
632 | if (ch == '#') { | |
633 | state = SCE_PL_COMMENTLINE; | |
634 | } else if (ch == '\"') { | |
635 | state = SCE_PL_STRING; | |
65ec6247 RD |
636 | Quote.New(1); |
637 | Quote.Open(ch); | |
f6bcfd97 BP |
638 | } else if (ch == '\'') { |
639 | state = SCE_PL_CHARACTER; | |
65ec6247 RD |
640 | Quote.New(1); |
641 | Quote.Open(ch); | |
f6bcfd97 BP |
642 | } else if (iswordstart(ch)) { |
643 | state = SCE_PL_WORD; | |
644 | preferRE = false; | |
65ec6247 RD |
645 | } else if (isPerlOperator(ch)) { |
646 | if (ch == ')' || ch == ']') | |
647 | preferRE = false; | |
648 | else | |
649 | preferRE = true; | |
f6bcfd97 BP |
650 | styler.ColourTo(i, SCE_PL_OPERATOR); |
651 | } | |
652 | } | |
653 | } | |
65ec6247 RD |
654 | if (state == SCE_PL_ERROR) { |
655 | break; | |
656 | } | |
f6bcfd97 BP |
657 | chPrev = ch; |
658 | } | |
1a2fb4cd | 659 | styler.ColourTo(lengthDoc - 1, state); |
f6bcfd97 BP |
660 | } |
661 | ||
a834585d RD |
662 | static const char * const perlWordListDesc[] = { |
663 | "Perl keywords", | |
664 | 0 | |
665 | }; | |
666 | ||
667 | LexerModule lmPerl(SCLEX_PERL, ColourisePerlDoc, "perl", 0, perlWordListDesc); |