]> git.saurik.com Git - wxWidgets.git/blame - src/stc/scintilla/src/LexPerl.cxx
Applied patch [ 677730 ] Menus with radio items (wxUniv)
[wxWidgets.git] / src / stc / scintilla / src / LexPerl.cxx
CommitLineData
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
22static inline bool isEOLChar(char ch) {
23 return (ch == '\r') || (ch == '\n');
24}
25
26static bool isSingleCharOp(char ch) {
27 char strCharSet[2];
28 strCharSet[0] = ch;
29 strCharSet[1] = '\0';
30 return (NULL != strstr("rwxoRWXOezsfdlpSbctugkTBMAC", strCharSet));
31}
32
33static 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
47static 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 65static inline bool isEndVar(char ch) {
f6bcfd97
BP
66 return !isalnum(ch) && ch != '#' && ch != '$' &&
67 ch != '_' && ch != '\'';
68}
69
70static 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
83static 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
95static 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
662static const char * const perlWordListDesc[] = {
663 "Perl keywords",
664 0
665};
666
667LexerModule lmPerl(SCLEX_PERL, ColourisePerlDoc, "perl", 0, perlWordListDesc);