PageRenderTime 1283ms CodeModel.GetById 181ms app.highlight 685ms RepoModel.GetById 276ms app.codeStats 1ms

/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexPerl.cxx

https://bitbucket.org/cwalther/cwe-ou-nobink
C++ | 1256 lines | 1152 code | 36 blank | 68 comment | 394 complexity | 6ca1cea59585876696d9cd2e78914b45 MD5 | raw file
   1// Scintilla source code edit control
   2/** @file LexPerl.cxx
   3 ** Lexer for subset of Perl.
   4 **/
   5// Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
   6// Lexical analysis fixes by Kein-Hong Man <mkh@pl.jaring.my>
   7// The License.txt file describes the conditions under which this software may be distributed.
   8
   9#include <stdlib.h>

  10#include <string.h>

  11#include <ctype.h>

  12#include <stdio.h>

  13#include <stdarg.h>

  14
  15#include "Platform.h"

  16
  17#include "PropSet.h"

  18#include "Accessor.h"

  19#include "KeyWords.h"

  20#include "Scintilla.h"

  21#include "SciLexer.h"

  22
  23#define PERLNUM_BINARY 1    // order is significant: 1-4 cannot have a dot
  24#define PERLNUM_HEX 2

  25#define PERLNUM_OCTAL 3

  26#define PERLNUM_FLOAT 4     // actually exponent part
  27#define PERLNUM_DECIMAL 5   // 1-5 are numbers; 6-7 are strings
  28#define PERLNUM_VECTOR 6

  29#define PERLNUM_V_VECTOR 7

  30#define PERLNUM_BAD 8

  31
  32#define BACK_NONE 0         // lookback state for bareword disambiguation:
  33#define BACK_OPERATOR 1     // whitespace/comments are insignificant
  34#define BACK_KEYWORD 2      // operators/keywords are needed for disambiguation
  35
  36#define HERE_DELIM_MAX 256

  37
  38static inline bool isEOLChar(char ch) {
  39	return (ch == '\r') || (ch == '\n');
  40}
  41
  42static bool isSingleCharOp(char ch) {
  43	char strCharSet[2];
  44	strCharSet[0] = ch;
  45	strCharSet[1] = '\0';
  46	return (NULL != strstr("rwxoRWXOezsfdlpSbctugkTBMAC", strCharSet));
  47}
  48
  49static inline bool isPerlOperator(char ch) {
  50	if (ch == '^' || ch == '&' || ch == '\\' ||
  51	        ch == '(' || ch == ')' || ch == '-' || ch == '+' ||
  52	        ch == '=' || ch == '|' || ch == '{' || ch == '}' ||
  53	        ch == '[' || ch == ']' || ch == ':' || ch == ';' ||
  54	        ch == '>' || ch == ',' ||
  55	        ch == '?' || ch == '!' || ch == '.' || ch == '~')
  56		return true;
  57	// these chars are already tested before this call
  58	// ch == '%' || ch == '*' || ch == '<' || ch == '/' ||
  59	return false;
  60}
  61
  62static bool isPerlKeyword(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler) {
  63	char s[100];
  64    unsigned int i, len = end - start;
  65    if (len > 30) { len = 30; }
  66	for (i = 0; i < len; i++, start++) s[i] = styler[start];
  67    s[i] = '\0';
  68	return keywords.InList(s);
  69}
  70
  71// Note: as lexer uses chars, UTF-8 bytes are considered as <0 values
  72// Note: iswordchar() was used in only one place in LexPerl, it is
  73// unnecessary as '.' is processed as the concatenation operator, so
  74// only isWordStart() is used in LexPerl
  75
  76static inline bool isWordStart(char ch) {
  77	return !isascii(ch) || isalnum(ch) || ch == '_';
  78}
  79
  80static inline bool isEndVar(char ch) {
  81	return isascii(ch) && !isalnum(ch) && ch != '#' && ch != '$' &&
  82	       ch != '_' && ch != '\'';
  83}
  84
  85static inline bool isNonQuote(char ch) {
  86	return !isascii(ch) || isalnum(ch) || ch == '_';
  87}
  88
  89static inline char actualNumStyle(int numberStyle) {
  90    if (numberStyle == PERLNUM_VECTOR || numberStyle == PERLNUM_V_VECTOR) {
  91        return SCE_PL_STRING;
  92    } else if (numberStyle == PERLNUM_BAD) {
  93        return SCE_PL_ERROR;
  94    }
  95    return SCE_PL_NUMBER;
  96}
  97
  98static bool isMatch(Accessor &styler, int lengthDoc, int pos, const char *val) {
  99	if ((pos + static_cast<int>(strlen(val))) >= lengthDoc) {
 100		return false;
 101	}
 102	while (*val) {
 103		if (*val != styler[pos++]) {
 104			return false;
 105		}
 106		val++;
 107	}
 108	return true;
 109}
 110
 111static char opposite(char ch) {
 112	if (ch == '(')
 113		return ')';
 114	if (ch == '[')
 115		return ']';
 116	if (ch == '{')
 117		return '}';
 118	if (ch == '<')
 119		return '>';
 120	return ch;
 121}
 122
 123static void ColourisePerlDoc(unsigned int startPos, int length, int initStyle,
 124                             WordList *keywordlists[], Accessor &styler) {
 125
 126	// Lexer for perl often has to backtrack to start of current style to determine
 127	// which characters are being used as quotes, how deeply nested is the
 128	// start position and what the termination string is for here documents
 129
 130	WordList &keywords = *keywordlists[0];
 131
 132	class HereDocCls {
 133	public:
 134		int State;		// 0: '<<' encountered
 135		// 1: collect the delimiter
 136		// 2: here doc text (lines after the delimiter)
 137		char Quote;		// the char after '<<'
 138		bool Quoted;		// true if Quote in ('\'','"','`')
 139		int DelimiterLength;	// strlen(Delimiter)
 140		char *Delimiter;	// the Delimiter, 256: sizeof PL_tokenbuf
 141		HereDocCls() {
 142			State = 0;
 143            Quote = 0;
 144            Quoted = false;
 145			DelimiterLength = 0;
 146			Delimiter = new char[HERE_DELIM_MAX];
 147			Delimiter[0] = '\0';
 148		}
 149		~HereDocCls() {
 150			delete []Delimiter;
 151		}
 152	};
 153	HereDocCls HereDoc;	// TODO: FIFO for stacked here-docs
 154
 155	class QuoteCls {
 156		public:
 157		int  Rep;
 158		int  Count;
 159		char Up;
 160		char Down;
 161		QuoteCls() {
 162			this->New(1);
 163		}
 164		void New(int r) {
 165			Rep   = r;
 166			Count = 0;
 167			Up    = '\0';
 168			Down  = '\0';
 169		}
 170		void Open(char u) {
 171			Count++;
 172			Up    = u;
 173			Down  = opposite(Up);
 174		}
 175	};
 176	QuoteCls Quote;
 177
 178	int state = initStyle;
 179	char numState = PERLNUM_DECIMAL;
 180	int dotCount = 0;
 181	unsigned int lengthDoc = startPos + length;
 182	//int sookedpos = 0; // these have no apparent use, see POD state
 183	//char sooked[100];
 184	//sooked[sookedpos] = '\0';
 185
 186	// If in a long distance lexical state, seek to the beginning to find quote characters
 187	// Perl strings can be multi-line with embedded newlines, so backtrack.
 188	// Perl numbers have additional state during lexing, so backtrack too.
 189	if (state == SCE_PL_HERE_Q || state == SCE_PL_HERE_QQ || state == SCE_PL_HERE_QX) {
 190		while ((startPos > 1) && (styler.StyleAt(startPos) != SCE_PL_HERE_DELIM)) {
 191			startPos--;
 192		}
 193		startPos = styler.LineStart(styler.GetLine(startPos));
 194		state = styler.StyleAt(startPos - 1);
 195	}
 196	if ( state == SCE_PL_STRING_Q
 197	|| state == SCE_PL_STRING_QQ
 198	|| state == SCE_PL_STRING_QX
 199	|| state == SCE_PL_STRING_QR
 200	|| state == SCE_PL_STRING_QW
 201	|| state == SCE_PL_REGEX
 202	|| state == SCE_PL_REGSUBST
 203	|| state == SCE_PL_STRING
 204	|| state == SCE_PL_BACKTICKS
 205	|| state == SCE_PL_CHARACTER
 206	|| state == SCE_PL_NUMBER
 207	|| state == SCE_PL_IDENTIFIER
 208    || state == SCE_PL_ERROR
 209	) {
 210		while ((startPos > 1) && (styler.StyleAt(startPos - 1) == state)) {
 211			startPos--;
 212		}
 213		state = SCE_PL_DEFAULT;
 214	}
 215
 216    // lookback at start of lexing to set proper state for backflag
 217    // after this, they are updated when elements are lexed
 218    int backflag = BACK_NONE;
 219    unsigned int backPos = startPos;
 220    if (backPos > 0) {
 221        backPos--;
 222        int sty = SCE_PL_DEFAULT;
 223        while ((backPos > 0) && (sty = styler.StyleAt(backPos),
 224               sty == SCE_PL_DEFAULT || sty == SCE_PL_COMMENTLINE))
 225            backPos--;
 226        if (sty == SCE_PL_OPERATOR)
 227            backflag = BACK_OPERATOR;
 228        else if (sty == SCE_PL_WORD)
 229            backflag = BACK_KEYWORD;
 230    }
 231
 232	styler.StartAt(startPos);
 233	char chPrev = styler.SafeGetCharAt(startPos - 1);
 234	if (startPos == 0)
 235		chPrev = '\n';
 236	char chNext = styler[startPos];
 237	styler.StartSegment(startPos);
 238
 239	for (unsigned int i = startPos; i < lengthDoc; i++) {
 240		char ch = chNext;
 241		// if the current character is not consumed due to the completion of an
 242		// earlier style, lexing can be restarted via a simple goto
 243	restartLexer:
 244		chNext = styler.SafeGetCharAt(i + 1);
 245		char chNext2 = styler.SafeGetCharAt(i + 2);
 246
 247		if (styler.IsLeadByte(ch)) {
 248			chNext = styler.SafeGetCharAt(i + 2);
 249			chPrev = ' ';
 250			i += 1;
 251			continue;
 252		}
 253		if ((chPrev == '\r' && ch == '\n')) {	// skip on DOS/Windows
 254			styler.ColourTo(i, state);
 255			chPrev = ch;
 256			continue;
 257		}
 258
 259		if (HereDoc.State == 1 && isEOLChar(ch)) {
 260			// Begin of here-doc (the line after the here-doc delimiter):
 261			// Lexically, the here-doc starts from the next line after the >>, but the
 262			// first line of here-doc seem to follow the style of the last EOL sequence
 263			HereDoc.State = 2;
 264			if (HereDoc.Quoted) {
 265				if (state == SCE_PL_HERE_DELIM) {
 266					// Missing quote at end of string! We are stricter than perl.
 267					// Colour here-doc anyway while marking this bit as an error.
 268					state = SCE_PL_ERROR;
 269				}
 270				styler.ColourTo(i - 1, state);
 271				switch (HereDoc.Quote) {
 272				case '\'':
 273					state = SCE_PL_HERE_Q ;
 274					break;
 275				case '"':
 276					state = SCE_PL_HERE_QQ;
 277					break;
 278				case '`':
 279					state = SCE_PL_HERE_QX;
 280					break;
 281				}
 282			} else {
 283				styler.ColourTo(i - 1, state);
 284				switch (HereDoc.Quote) {
 285				case '\\':
 286					state = SCE_PL_HERE_Q ;
 287					break;
 288				default :
 289					state = SCE_PL_HERE_QQ;
 290				}
 291			}
 292		}
 293
 294		if (state == SCE_PL_DEFAULT) {
 295			if ((isascii(ch) && isdigit(ch)) || (isascii(chNext) && isdigit(chNext) &&
 296				(ch == '.' || ch == 'v'))) {
 297				state = SCE_PL_NUMBER;
 298                backflag = BACK_NONE;
 299				numState = PERLNUM_DECIMAL;
 300				dotCount = 0;
 301				if (ch == '0') {	// hex,bin,octal
 302					if (chNext == 'x') {
 303						numState = PERLNUM_HEX;
 304					} else if (chNext == 'b') {
 305                        numState = PERLNUM_BINARY;
 306                    } else if (isascii(chNext) && isdigit(chNext)) {
 307                        numState = PERLNUM_OCTAL;
 308                    }
 309                    if (numState != PERLNUM_DECIMAL) {
 310						i++;
 311						ch = chNext;
 312						chNext = chNext2;
 313                    }
 314				} else if (ch == 'v') {	// vector
 315					numState = PERLNUM_V_VECTOR;
 316				}
 317			} else if (isWordStart(ch)) {
 318                // if immediately prefixed by '::', always a bareword
 319                state = SCE_PL_WORD;
 320                if (chPrev == ':' && styler.SafeGetCharAt(i - 2) == ':') {
 321                    state = SCE_PL_IDENTIFIER;
 322                }
 323                unsigned int kw = i + 1;
 324                // first check for possible quote-like delimiter
 325				if (ch == 's' && !isNonQuote(chNext)) {
 326					state = SCE_PL_REGSUBST;
 327					Quote.New(2);
 328				} else if (ch == 'm' && !isNonQuote(chNext)) {
 329					state = SCE_PL_REGEX;
 330					Quote.New(1);
 331				} else if (ch == 'q' && !isNonQuote(chNext)) {
 332					state = SCE_PL_STRING_Q;
 333					Quote.New(1);
 334				} else if (ch == 'y' && !isNonQuote(chNext)) {
 335					state = SCE_PL_REGSUBST;
 336					Quote.New(2);
 337				} else if (ch == 't' && chNext == 'r' && !isNonQuote(chNext2)) {
 338					state = SCE_PL_REGSUBST;
 339					Quote.New(2);
 340                    kw++;
 341				} else if (ch == 'q' && (chNext == 'q' || chNext == 'r' || chNext == 'w' || chNext == 'x') && !isNonQuote(chNext2)) {
 342					if      (chNext == 'q') state = SCE_PL_STRING_QQ;
 343					else if (chNext == 'x') state = SCE_PL_STRING_QX;
 344					else if (chNext == 'r') state = SCE_PL_STRING_QR;
 345					else if (chNext == 'w') state = SCE_PL_STRING_QW;
 346					Quote.New(1);
 347                    kw++;
 348				} else if (ch == 'x' && (chNext == '=' ||	// repetition
 349                           !isWordStart(chNext) ||
 350                           (isdigit(chPrev) && isdigit(chNext)))) {
 351                    state = SCE_PL_OPERATOR;
 352                }
 353                // if potentially a keyword, scan forward and grab word, then check
 354                // if it's really one; if yes, disambiguation test is performed
 355                // otherwise it is always a bareword and we skip a lot of scanning
 356                // note: keywords assumed to be limited to [_a-zA-Z] only
 357                if (state == SCE_PL_WORD) {
 358                    while (isWordStart(styler.SafeGetCharAt(kw))) kw++;
 359                    if (!isPerlKeyword(styler.GetStartSegment(), kw, keywords, styler)) {
 360                        state = SCE_PL_IDENTIFIER;
 361                    }
 362                }
 363                // if already SCE_PL_IDENTIFIER, then no ambiguity, skip this
 364                // for quote-like delimiters/keywords, attempt to disambiguate
 365                // to select for bareword, change state -> SCE_PL_IDENTIFIER
 366                if (state != SCE_PL_IDENTIFIER && i > 0) {
 367                    unsigned int j = i;
 368                    bool moreback = false;      // true if passed newline/comments
 369                    bool brace = false;         // true if opening brace found
 370                    char ch2;
 371                    // first look backwards past whitespace/comments for EOLs
 372                    // if BACK_NONE, neither operator nor keyword, so skip test
 373                    if (backflag != BACK_NONE) {
 374                        while (--j > backPos) {
 375                            if (isEOLChar(styler.SafeGetCharAt(j)))
 376                                moreback = true;
 377                        }
 378                        ch2 = styler.SafeGetCharAt(j);
 379                        if (ch2 == '{' && !moreback) {
 380                            // {bareword: possible variable spec
 381                            brace = true;
 382                        } else if ((ch2 == '&' && styler.SafeGetCharAt(j - 1) != '&')
 383                                // &bareword: subroutine call
 384                                || (ch2 == '>' && styler.SafeGetCharAt(j - 1) == '-')
 385                                // ->bareword: part of variable spec
 386                                || (ch2 == 'b' && styler.Match(j - 2, "su"))) {
 387                                // sub bareword: subroutine declaration
 388                                // (implied BACK_KEYWORD, no keywords end in 'sub'!)
 389                            state = SCE_PL_IDENTIFIER;
 390                        }
 391                        // if status still ambiguous, look forward after word past
 392                        // tabs/spaces only; if ch2 isn't one of '[{(,' it can never
 393                        // match anything, so skip the whole thing
 394                        j = kw;
 395                        if (state != SCE_PL_IDENTIFIER
 396                            && (ch2 == '{' || ch2 == '(' || ch2 == '['|| ch2 == ',')
 397                            && kw < lengthDoc) {
 398                            while (ch2 = styler.SafeGetCharAt(j),
 399                                   (ch2 == ' ' || ch2 == '\t') && j < lengthDoc) {
 400                                j++;
 401                            }
 402                            if ((ch2 == '}' && brace)
 403                             // {bareword}: variable spec
 404                             || (ch2 == '=' && styler.SafeGetCharAt(j + 1) == '>')) {
 405                             // [{(, bareword=>: hash literal
 406                                state = SCE_PL_IDENTIFIER;
 407                            }
 408                        }
 409                    }
 410                }
 411                backflag = BACK_NONE;
 412                // an identifier or bareword
 413                if (state == SCE_PL_IDENTIFIER) {
 414                    if ((!isWordStart(chNext) && chNext != '\'')
 415                        || (chNext == '.' && chNext2 == '.')) {
 416                        // We need that if length of word == 1!
 417                        // This test is copied from the SCE_PL_WORD handler.
 418                        styler.ColourTo(i, SCE_PL_IDENTIFIER);
 419                        state = SCE_PL_DEFAULT;
 420                    }
 421                // a keyword
 422                } else if (state == SCE_PL_WORD) {
 423                    i = kw - 1;
 424                    if (ch == '_' && chNext == '_' &&
 425                        (isMatch(styler, lengthDoc, styler.GetStartSegment(), "__DATA__")
 426                      || isMatch(styler, lengthDoc, styler.GetStartSegment(), "__END__"))) {
 427                        styler.ColourTo(i, SCE_PL_DATASECTION);
 428                        state = SCE_PL_DATASECTION;
 429                    } else {
 430                        styler.ColourTo(i, SCE_PL_WORD);
 431                        state = SCE_PL_DEFAULT;
 432                        backflag = BACK_KEYWORD;
 433                        backPos = i;
 434                    }
 435                    ch = styler.SafeGetCharAt(i);
 436                    chNext = styler.SafeGetCharAt(i + 1);
 437                // a repetition operator 'x'
 438                } else if (state == SCE_PL_OPERATOR) {
 439                    styler.ColourTo(i, SCE_PL_OPERATOR);
 440                    state = SCE_PL_DEFAULT;
 441                // quote-like delimiter, skip one char if double-char delimiter
 442                } else {
 443                    i = kw - 1;
 444                    chNext = styler.SafeGetCharAt(i + 1);
 445                }
 446			} else if (ch == '#') {
 447				state = SCE_PL_COMMENTLINE;
 448			} else if (ch == '\"') {
 449				state = SCE_PL_STRING;
 450				Quote.New(1);
 451				Quote.Open(ch);
 452                backflag = BACK_NONE;
 453			} else if (ch == '\'') {
 454				if (chPrev == '&') {
 455					// Archaic call
 456					styler.ColourTo(i, state);
 457				} else {
 458					state = SCE_PL_CHARACTER;
 459					Quote.New(1);
 460					Quote.Open(ch);
 461				}
 462                backflag = BACK_NONE;
 463			} else if (ch == '`') {
 464				state = SCE_PL_BACKTICKS;
 465				Quote.New(1);
 466				Quote.Open(ch);
 467                backflag = BACK_NONE;
 468			} else if (ch == '$') {
 469				if ((chNext == '{') || isspacechar(chNext)) {
 470					styler.ColourTo(i, SCE_PL_SCALAR);
 471				} else {
 472					state = SCE_PL_SCALAR;
 473					if ((chNext == '`' && chNext2 == '`')
 474                     || (chNext == ':' && chNext2 == ':')) {
 475						i += 2;
 476						ch = styler.SafeGetCharAt(i);
 477						chNext = styler.SafeGetCharAt(i + 1);
 478					} else {
 479						i++;
 480						ch = chNext;
 481						chNext = chNext2;
 482					}
 483				}
 484                backflag = BACK_NONE;
 485			} else if (ch == '@') {
 486				if (!isascii(chNext) || isalpha(chNext) || chNext == '#' || chNext == '$'
 487					|| chNext == '_' || chNext == '+' || chNext == '-') {
 488					state = SCE_PL_ARRAY;
 489                } else if (chNext == ':' && chNext2 == ':') {
 490                    state = SCE_PL_ARRAY;
 491                    i += 2;
 492                    ch = styler.SafeGetCharAt(i);
 493                    chNext = styler.SafeGetCharAt(i + 1);
 494				} else if (chNext != '{' && chNext != '[') {
 495					styler.ColourTo(i, SCE_PL_ARRAY);
 496				} else {
 497					styler.ColourTo(i, SCE_PL_ARRAY);
 498				}
 499                backflag = BACK_NONE;
 500			} else if (ch == '%') {
 501				if (!isascii(chNext) || isalpha(chNext) || chNext == '#' || chNext == '$'
 502                    || chNext == '_' || chNext == '!' || chNext == '^') {
 503					state = SCE_PL_HASH;
 504                    i++;
 505                    ch = chNext;
 506                    chNext = chNext2;
 507                } else if (chNext == ':' && chNext2 == ':') {
 508                    state = SCE_PL_HASH;
 509                    i += 2;
 510                    ch = styler.SafeGetCharAt(i);
 511                    chNext = styler.SafeGetCharAt(i + 1);
 512				} else if (chNext == '{') {
 513					styler.ColourTo(i, SCE_PL_HASH);
 514				} else {
 515					styler.ColourTo(i, SCE_PL_OPERATOR);
 516				}
 517                backflag = BACK_NONE;
 518			} else if (ch == '*') {
 519                char strch[2];
 520                strch[0] = chNext;
 521                strch[1] = '\0';
 522                if (chNext == ':' && chNext2 == ':') {
 523                    state = SCE_PL_SYMBOLTABLE;
 524                    i += 2;
 525                    ch = styler.SafeGetCharAt(i);
 526                    chNext = styler.SafeGetCharAt(i + 1);
 527				} else if (!isascii(chNext) || isalpha(chNext) || chNext == '_'
 528                        || NULL != strstr("^/|,\\\";#%^:?<>)[]", strch)) {
 529					state = SCE_PL_SYMBOLTABLE;
 530                    i++;
 531                    ch = chNext;
 532                    chNext = chNext2;
 533				} else if (chNext == '{') {
 534					styler.ColourTo(i, SCE_PL_SYMBOLTABLE);
 535				} else {
 536					if (chNext == '*') {	// exponentiation
 537						i++;
 538						ch = chNext;
 539						chNext = chNext2;
 540					}
 541					styler.ColourTo(i, SCE_PL_OPERATOR);
 542				}
 543                backflag = BACK_NONE;
 544			} else if (ch == '/' || (ch == '<' && chNext == '<')) {
 545				// Explicit backward peeking to set a consistent preferRE for
 546				// any slash found, so no longer need to track preferRE state.
 547				// Find first previous significant lexed element and interpret.
 548                // Test for HERE doc start '<<' shares this code, helps to
 549                // determine if it should be an operator.
 550				bool preferRE = false;
 551                bool isHereDoc = (ch == '<');
 552                bool hereDocSpace = false;      // these are for corner case:
 553                bool hereDocScalar = false;     // SCALAR [whitespace] '<<'
 554				unsigned int bk = (i > 0)? i - 1: 0;
 555				char bkch;
 556				styler.Flush();
 557                if (styler.StyleAt(bk) == SCE_PL_DEFAULT)
 558                    hereDocSpace = true;
 559				while ((bk > 0) && (styler.StyleAt(bk) == SCE_PL_DEFAULT ||
 560					styler.StyleAt(bk) == SCE_PL_COMMENTLINE)) {
 561					bk--;
 562				}
 563				if (bk == 0) {
 564					// position 0 won't really be checked; rarely happens
 565					// hard to fix due to an unsigned index i
 566					preferRE = true;
 567				} else {
 568					int bkstyle = styler.StyleAt(bk);
 569					bkch = styler.SafeGetCharAt(bk);
 570					switch(bkstyle) {
 571					case SCE_PL_OPERATOR:
 572						preferRE = true;
 573						if (bkch == ')' || bkch == ']') {
 574							preferRE = false;
 575						} else if (bkch == '}') {
 576							// backtrack further, count balanced brace pairs
 577							// if a brace pair found, see if it's a variable
 578							int braceCount = 1;
 579							while (--bk > 0) {
 580								bkstyle = styler.StyleAt(bk);
 581								if (bkstyle == SCE_PL_OPERATOR) {
 582									bkch = styler.SafeGetCharAt(bk);
 583									if (bkch == ';') {	// early out
 584										break;
 585									} else if (bkch == '}') {
 586										braceCount++;
 587									} else if (bkch == '{') {
 588										if (--braceCount == 0)
 589											break;
 590									}
 591								}
 592							}
 593							if (bk == 0) {
 594								// at beginning, true
 595							} else if (braceCount == 0) {
 596								// balanced { found, bk>0, skip more whitespace
 597								if (styler.StyleAt(--bk) == SCE_PL_DEFAULT) {
 598									while (bk > 0) {
 599										bkstyle = styler.StyleAt(--bk);
 600										if (bkstyle != SCE_PL_DEFAULT)
 601											break;
 602									}
 603								}
 604								bkstyle = styler.StyleAt(bk);
 605								if (bkstyle == SCE_PL_SCALAR
 606								 || bkstyle == SCE_PL_ARRAY
 607								 || bkstyle == SCE_PL_HASH
 608								 || bkstyle == SCE_PL_SYMBOLTABLE
 609								 || bkstyle == SCE_PL_OPERATOR) {
 610									preferRE = false;
 611								}
 612							}
 613						}
 614						break;
 615					case SCE_PL_IDENTIFIER:
 616						preferRE = true;
 617						if (bkch == '>') {	// inputsymbol
 618							preferRE = false;
 619							break;
 620						}
 621						// backtrack to find "->" or "::" before identifier
 622						while (bk > 0 && styler.StyleAt(bk) == SCE_PL_IDENTIFIER) {
 623							bk--;
 624						}
 625						while (bk > 0) {
 626							bkstyle = styler.StyleAt(bk);
 627							if (bkstyle == SCE_PL_DEFAULT ||
 628							    bkstyle == SCE_PL_COMMENTLINE) {
 629							} else if (bkstyle == SCE_PL_OPERATOR) {
 630								// gcc 3.2.3 bloats if more compact form used
 631								bkch = styler.SafeGetCharAt(bk);
 632								if (bkch == '>') { // "->"
 633									if (styler.SafeGetCharAt(bk - 1) == '-') {
 634										preferRE = false;
 635										break;
 636									}
 637								} else if (bkch == ':') { // "::"
 638									if (styler.SafeGetCharAt(bk - 1) == ':') {
 639										preferRE = false;
 640										break;
 641									}
 642								}
 643							} else {// bare identifier, usually a function call but Perl
 644								// optimizes them as pseudo-constants, then the next
 645								// '/' will be a divide; favour divide over regex
 646								// if there is a whitespace after the '/'
 647								if (isspacechar(chNext)) {
 648									preferRE = false;
 649								}
 650								break;
 651							}
 652							bk--;
 653						}
 654						break;
 655                    case SCE_PL_SCALAR:     // for $var<< case
 656                        hereDocScalar = true;
 657                        break;
 658					// other styles uses the default, preferRE=false
 659					case SCE_PL_WORD:
 660					case SCE_PL_POD:
 661					case SCE_PL_POD_VERB:
 662					case SCE_PL_HERE_Q:
 663					case SCE_PL_HERE_QQ:
 664					case SCE_PL_HERE_QX:
 665						preferRE = true;
 666						break;
 667					}
 668				}
 669                if (isHereDoc) {    // handle HERE doc
 670                    // if SCALAR whitespace '<<', *always* a HERE doc
 671                    if (preferRE || (hereDocSpace && hereDocScalar)) {
 672                        state = SCE_PL_HERE_DELIM;
 673                        HereDoc.State = 0;
 674                    } else {        // << operator
 675						i++;
 676						ch = chNext;
 677						chNext = chNext2;
 678                        styler.ColourTo(i, SCE_PL_OPERATOR);
 679                    }
 680                } else {            // handle regexp
 681                    if (preferRE) {
 682                        state = SCE_PL_REGEX;
 683                        Quote.New(1);
 684                        Quote.Open(ch);
 685                    } else {        // / operator
 686                        styler.ColourTo(i, SCE_PL_OPERATOR);
 687                    }
 688                }
 689                backflag = BACK_NONE;
 690			} else if (ch == '<') {
 691				// looks forward for matching > on same line
 692				unsigned int fw = i + 1;
 693				while (fw < lengthDoc) {
 694					char fwch = styler.SafeGetCharAt(fw);
 695					if (fwch == ' ') {
 696						if (styler.SafeGetCharAt(fw-1) != '\\' ||
 697						    styler.SafeGetCharAt(fw-2) != '\\')
 698						break;
 699					} else if (isEOLChar(fwch) || isspacechar(fwch)) {
 700						break;
 701					} else if (fwch == '>') {
 702						if ((fw - i) == 2 &&	// '<=>' case
 703						    styler.SafeGetCharAt(fw-1) == '=') {
 704							styler.ColourTo(fw, SCE_PL_OPERATOR);
 705						} else {
 706							styler.ColourTo(fw, SCE_PL_IDENTIFIER);
 707						}
 708						i = fw;
 709						ch = fwch;
 710						chNext = styler.SafeGetCharAt(i+1);
 711					}
 712					fw++;
 713				}
 714				styler.ColourTo(i, SCE_PL_OPERATOR);
 715                backflag = BACK_NONE;
 716			} else if (ch == '='	// POD
 717			           && isalpha(chNext)
 718			           && (isEOLChar(chPrev))) {
 719				state = SCE_PL_POD;
 720                backflag = BACK_NONE;
 721				//sookedpos = 0;
 722				//sooked[sookedpos] = '\0';
 723			} else if (ch == '-'	// file test operators
 724			           && isSingleCharOp(chNext)
 725			           && !isalnum((chNext2 = styler.SafeGetCharAt(i+2)))) {
 726				styler.ColourTo(i + 1, SCE_PL_WORD);
 727				state = SCE_PL_DEFAULT;
 728				i++;
 729				ch = chNext;
 730				chNext = chNext2;
 731                backflag = BACK_NONE;
 732			} else if (isPerlOperator(ch)) {
 733				if (ch == '.' && chNext == '.') { // .. and ...
 734					i++;
 735					if (chNext2 == '.') { i++; }
 736					state = SCE_PL_DEFAULT;
 737					ch = styler.SafeGetCharAt(i);
 738					chNext = styler.SafeGetCharAt(i + 1);
 739				}
 740				styler.ColourTo(i, SCE_PL_OPERATOR);
 741                backflag = BACK_OPERATOR;
 742                backPos = i;
 743			} else {
 744				// keep colouring defaults to make restart easier
 745				styler.ColourTo(i, SCE_PL_DEFAULT);
 746			}
 747		} else if (state == SCE_PL_NUMBER) {
 748			if (ch == '.') {
 749				if (chNext == '.') {
 750					// double dot is always an operator
 751					goto numAtEnd;
 752				} else if (numState <= PERLNUM_FLOAT) {
 753					// non-decimal number or float exponent, consume next dot
 754					styler.ColourTo(i - 1, SCE_PL_NUMBER);
 755					styler.ColourTo(i, SCE_PL_OPERATOR);
 756					state = SCE_PL_DEFAULT;
 757				} else { // decimal or vectors allows dots
 758					dotCount++;
 759					if (numState == PERLNUM_DECIMAL) {
 760						if (dotCount > 1) {
 761							if (isdigit(chNext)) { // really a vector
 762								numState = PERLNUM_VECTOR;
 763							} else	// number then dot
 764								goto numAtEnd;
 765						}
 766					} else { // vectors
 767						if (!isdigit(chNext))	// vector then dot
 768							goto numAtEnd;
 769					}
 770				}
 771			} else if (ch == '_' && numState == PERLNUM_DECIMAL) {
 772				if (!isdigit(chNext)) {
 773					goto numAtEnd;
 774				}
 775			} else if (!isascii(ch) || isalnum(ch)) {
 776				if (numState == PERLNUM_VECTOR || numState == PERLNUM_V_VECTOR) {
 777					if (!isascii(ch) || isalpha(ch)) {
 778						if (dotCount == 0) { // change to word
 779							state = SCE_PL_IDENTIFIER;
 780						} else { // vector then word
 781							goto numAtEnd;
 782						}
 783					}
 784				} else if (numState == PERLNUM_DECIMAL) {
 785					if (ch == 'E' || ch == 'e') { // exponent
 786						numState = PERLNUM_FLOAT;
 787						if (chNext == '+' || chNext == '-') {
 788							i++;
 789							ch = chNext;
 790							chNext = chNext2;
 791						}
 792					} else if (!isascii(ch) || !isdigit(ch)) { // number then word
 793						goto numAtEnd;
 794					}
 795				} else if (numState == PERLNUM_FLOAT) {
 796					if (!isdigit(ch)) { // float then word
 797						goto numAtEnd;
 798					}
 799				} else if (numState == PERLNUM_OCTAL) {
 800                    if (!isdigit(ch))
 801                        goto numAtEnd;
 802                    else if (ch > '7')
 803                        numState = PERLNUM_BAD;
 804                } else if (numState == PERLNUM_BINARY) {
 805                    if (!isdigit(ch))
 806                        goto numAtEnd;
 807                    else if (ch > '1')
 808                        numState = PERLNUM_BAD;
 809                } else if (numState == PERLNUM_HEX) {
 810                    int ch2 = toupper(ch);
 811                    if (!isdigit(ch) && !(ch2 >= 'A' && ch2 <= 'F'))
 812                        goto numAtEnd;
 813				} else {//(numState == PERLNUM_BAD) {
 814                    if (!isdigit(ch))
 815                        goto numAtEnd;
 816                }
 817			} else {
 818				// complete current number or vector
 819			numAtEnd:
 820				styler.ColourTo(i - 1, actualNumStyle(numState));
 821				state = SCE_PL_DEFAULT;
 822				goto restartLexer;
 823			}
 824		} else if (state == SCE_PL_IDENTIFIER) {
 825			if (!isWordStart(chNext) && chNext != '\'') {
 826				styler.ColourTo(i, SCE_PL_IDENTIFIER);
 827				state = SCE_PL_DEFAULT;
 828				ch = ' ';
 829			}
 830		} else {
 831			if (state == SCE_PL_COMMENTLINE) {
 832				if (isEOLChar(ch)) {
 833					styler.ColourTo(i - 1, state);
 834					state = SCE_PL_DEFAULT;
 835					goto restartLexer;
 836				} else if (isEOLChar(chNext)) {
 837					styler.ColourTo(i, state);
 838					state = SCE_PL_DEFAULT;
 839				}
 840			} else if (state == SCE_PL_HERE_DELIM) {
 841				//
 842				// From perldata.pod:
 843				// ------------------
 844				// A line-oriented form of quoting is based on the shell ``here-doc''
 845				// syntax.
 846				// Following a << you specify a string to terminate the quoted material,
 847				// and all lines following the current line down to the terminating
 848				// string are the value of the item.
 849				// The terminating string may be either an identifier (a word),
 850				// or some quoted text.
 851				// If quoted, the type of quotes you use determines the treatment of
 852				// the text, just as in regular quoting.
 853				// An unquoted identifier works like double quotes.
 854				// There must be no space between the << and the identifier.
 855				// (If you put a space it will be treated as a null identifier,
 856				// which is valid, and matches the first empty line.)
 857				// (This is deprecated, -w warns of this syntax)
 858				// The terminating string must appear by itself (unquoted and with no
 859				// surrounding whitespace) on the terminating line.
 860				//
 861				// From Bash info:
 862				// ---------------
 863				// Specifier format is: <<[-]WORD
 864				// Optional '-' is for removal of leading tabs from here-doc.
 865				// Whitespace acceptable after <<[-] operator.
 866				//
 867				if (HereDoc.State == 0) { // '<<' encountered
 868                    bool gotspace = false;
 869                    unsigned int oldi = i;
 870                    if (chNext == ' ' || chNext == '\t') {
 871                        // skip whitespace; legal for quoted delimiters
 872                        gotspace = true;
 873                        do {
 874                            i++;
 875                            chNext = styler.SafeGetCharAt(i + 1);
 876                        } while ((i + 1 < lengthDoc) && (chNext == ' ' || chNext == '\t'));
 877                        chNext2 = styler.SafeGetCharAt(i + 2);
 878                    }
 879					HereDoc.State = 1;
 880					HereDoc.Quote = chNext;
 881					HereDoc.Quoted = false;
 882					HereDoc.DelimiterLength = 0;
 883					HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0';
 884					if (chNext == '\'' || chNext == '"' || chNext == '`') {
 885                        // a quoted here-doc delimiter
 886						i++;
 887						ch = chNext;
 888						chNext = chNext2;
 889						HereDoc.Quoted = true;
 890					} else if (isspacechar(chNext) || isdigit(chNext) || chNext == '\\'
 891						|| chNext == '=' || chNext == '$' || chNext == '@'
 892                        || ((isalpha(chNext) || chNext == '_') && gotspace)) {
 893						// left shift << or <<= operator cases
 894                        // restore position if operator
 895                        i = oldi;
 896						styler.ColourTo(i, SCE_PL_OPERATOR);
 897						state = SCE_PL_DEFAULT;
 898						HereDoc.State = 0;
 899                        goto restartLexer;
 900					} else {
 901						// an unquoted here-doc delimiter, no special handling
 902                        // (cannot be prefixed by spaces/tabs), or
 903						// symbols terminates; deprecated zero-length delimiter
 904					}
 905
 906				} else if (HereDoc.State == 1) { // collect the delimiter
 907                    backflag = BACK_NONE;
 908					if (HereDoc.Quoted) { // a quoted here-doc delimiter
 909						if (ch == HereDoc.Quote) { // closing quote => end of delimiter
 910							styler.ColourTo(i, state);
 911							state = SCE_PL_DEFAULT;
 912						} else {
 913							if (ch == '\\' && chNext == HereDoc.Quote) { // escaped quote
 914								i++;
 915								ch = chNext;
 916								chNext = chNext2;
 917							}
 918							HereDoc.Delimiter[HereDoc.DelimiterLength++] = ch;
 919							HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0';
 920						}
 921					} else { // an unquoted here-doc delimiter
 922						if (isalnum(ch) || ch == '_') {
 923							HereDoc.Delimiter[HereDoc.DelimiterLength++] = ch;
 924							HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0';
 925						} else {
 926							styler.ColourTo(i - 1, state);
 927							state = SCE_PL_DEFAULT;
 928							goto restartLexer;
 929						}
 930					}
 931					if (HereDoc.DelimiterLength >= HERE_DELIM_MAX - 1) {
 932						styler.ColourTo(i - 1, state);
 933						state = SCE_PL_ERROR;
 934						goto restartLexer;
 935					}
 936				}
 937			} else if (HereDoc.State == 2) {
 938				// state == SCE_PL_HERE_Q || state == SCE_PL_HERE_QQ || state == SCE_PL_HERE_QX
 939				if (isEOLChar(chPrev) && isMatch(styler, lengthDoc, i, HereDoc.Delimiter)) {
 940					i += HereDoc.DelimiterLength;
 941					chPrev = styler.SafeGetCharAt(i - 1);
 942					ch = styler.SafeGetCharAt(i);
 943					if (isEOLChar(ch)) {
 944						styler.ColourTo(i - 1, state);
 945						state = SCE_PL_DEFAULT;
 946                        backflag = BACK_NONE;
 947						HereDoc.State = 0;
 948						goto restartLexer;
 949					}
 950					chNext = styler.SafeGetCharAt(i + 1);
 951				}
 952			} else if (state == SCE_PL_POD
 953				|| state == SCE_PL_POD_VERB) {
 954				if (isEOLChar(chPrev)) {
 955					if (ch == ' ' || ch == '\t') {
 956						styler.ColourTo(i - 1, state);
 957						state = SCE_PL_POD_VERB;
 958					} else {
 959						styler.ColourTo(i - 1, state);
 960						state = SCE_PL_POD;
 961						if (ch == '=') {
 962							if (isMatch(styler, lengthDoc, i, "=cut")) {
 963								styler.ColourTo(i - 1 + 4, state);
 964								i += 4;
 965								state = SCE_PL_DEFAULT;
 966								ch = styler.SafeGetCharAt(i);
 967								//chNext = styler.SafeGetCharAt(i + 1);
 968								goto restartLexer;
 969							}
 970						}
 971					}
 972				}
 973			} else if (state == SCE_PL_SCALAR	// variable names
 974				|| state == SCE_PL_ARRAY
 975				|| state == SCE_PL_HASH
 976				|| state == SCE_PL_SYMBOLTABLE) {
 977				if (ch == ':' && chNext == ':') {	// skip ::
 978					i++;
 979					ch = chNext;
 980					chNext = chNext2;
 981				}
 982				else if (isEndVar(ch)) {
 983					if (i == (styler.GetStartSegment() + 1)) {
 984						// Special variable: $(, $_ etc.
 985						styler.ColourTo(i, state);
 986						state = SCE_PL_DEFAULT;
 987					} else {
 988						styler.ColourTo(i - 1, state);
 989						state = SCE_PL_DEFAULT;
 990						goto restartLexer;
 991					}
 992				}
 993			} else if (state == SCE_PL_REGEX
 994				|| state == SCE_PL_STRING_QR
 995				) {
 996				if (!Quote.Up && !isspacechar(ch)) {
 997					Quote.Open(ch);
 998				} else if (ch == '\\' && Quote.Up != '\\') {
 999					// SG: Is it save to skip *every* escaped char?
1000					i++;
1001					ch = chNext;
1002					chNext = styler.SafeGetCharAt(i + 1);
1003				} else {
1004					if (ch == Quote.Down /*&& chPrev != '\\'*/) {
1005						Quote.Count--;
1006						if (Quote.Count == 0) {
1007							Quote.Rep--;
1008							if (Quote.Up == Quote.Down) {
1009								Quote.Count++;
1010							}
1011						}
1012						if (!isalpha(chNext)) {
1013							if (Quote.Rep <= 0) {
1014								styler.ColourTo(i, state);
1015								state = SCE_PL_DEFAULT;
1016								ch = ' ';
1017							}
1018						}
1019					} else if (ch == Quote.Up /*&& chPrev != '\\'*/) {
1020						Quote.Count++;
1021					} else if (!isascii(chNext) || !isalpha(chNext)) {
1022						if (Quote.Rep <= 0) {
1023							styler.ColourTo(i, state);
1024							state = SCE_PL_DEFAULT;
1025							ch = ' ';
1026						}
1027					}
1028				}
1029			} else if (state == SCE_PL_REGSUBST) {
1030				if (!Quote.Up && !isspacechar(ch)) {
1031					Quote.Open(ch);
1032				} else if (ch == '\\' && Quote.Up != '\\') {
1033					// SG: Is it save to skip *every* escaped char?
1034					i++;
1035					ch = chNext;
1036					chNext = styler.SafeGetCharAt(i + 1);
1037				} else {
1038					if (Quote.Count == 0 && Quote.Rep == 1) {
1039						/* We matched something like s(...) or tr{...}
1040						* and are looking for the next matcher characters,
1041						* which could be either bracketed ({...}) or non-bracketed
1042						* (/.../).
1043						*
1044						* Number-signs are problematic.  If they occur after
1045						* the close of the first part, treat them like
1046						* a Quote.Up char, even if they actually start comments.
1047						*
1048						* If we find an alnum, we end the regsubst, and punt.
1049						*
1050						* Eric Promislow   ericp@activestate.com  Aug 9,2000
1051						*/
1052						if (isspacechar(ch)) {
1053							// Keep going
1054						}
1055						else if (!isascii(ch) || isalnum(ch)) {
1056							styler.ColourTo(i, state);
1057							state = SCE_PL_DEFAULT;
1058							ch = ' ';
1059						} else {
1060							Quote.Open(ch);
1061						}
1062					} else if (ch == Quote.Down /*&& chPrev != '\\'*/) {
1063						Quote.Count--;
1064						if (Quote.Count == 0) {
1065							Quote.Rep--;
1066						}
1067						if (!isascii(chNext) || !isalpha(chNext)) {
1068							if (Quote.Rep <= 0) {
1069								styler.ColourTo(i, state);
1070								state = SCE_PL_DEFAULT;
1071								ch = ' ';
1072							}
1073						}
1074						if (Quote.Up == Quote.Down) {
1075							Quote.Count++;
1076						}
1077					} else if (ch == Quote.Up /*&& chPrev != '\\'*/) {
1078						Quote.Count++;
1079					} else if (!isascii(chNext) || !isalpha(chNext)) {
1080						if (Quote.Rep <= 0) {
1081							styler.ColourTo(i, state);
1082							state = SCE_PL_DEFAULT;
1083							ch = ' ';
1084						}
1085					}
1086				}
1087			} else if (state == SCE_PL_STRING_Q
1088				|| state == SCE_PL_STRING_QQ
1089				|| state == SCE_PL_STRING_QX
1090				|| state == SCE_PL_STRING_QW
1091				|| state == SCE_PL_STRING
1092				|| state == SCE_PL_CHARACTER
1093				|| state == SCE_PL_BACKTICKS
1094				) {
1095				if (!Quote.Down && !isspacechar(ch)) {
1096					Quote.Open(ch);
1097				} else if (ch == '\\' && Quote.Up != '\\') {
1098					i++;
1099					ch = chNext;
1100					chNext = styler.SafeGetCharAt(i + 1);
1101				} else if (ch == Quote.Down) {
1102					Quote.Count--;
1103					if (Quote.Count == 0) {
1104						Quote.Rep--;
1105						if (Quote.Rep <= 0) {
1106							styler.ColourTo(i, state);
1107							state = SCE_PL_DEFAULT;
1108							ch = ' ';
1109						}
1110						if (Quote.Up == Quote.Down) {
1111							Quote.Count++;
1112						}
1113					}
1114				} else if (ch == Quote.Up) {
1115					Quote.Count++;
1116				}
1117			}
1118		}
1119		if (state == SCE_PL_ERROR) {
1120			break;
1121		}
1122		chPrev = ch;
1123	}
1124	styler.ColourTo(lengthDoc - 1, state);
1125}
1126
1127static bool IsCommentLine(int line, Accessor &styler) {
1128	int pos = styler.LineStart(line);
1129	int eol_pos = styler.LineStart(line + 1) - 1;
1130	for (int i = pos; i < eol_pos; i++) {
1131		char ch = styler[i];
1132        int style = styler.StyleAt(i);
1133		if (ch == '#' && style == SCE_PL_COMMENTLINE)
1134			return true;
1135		else if (ch != ' ' && ch != '\t')
1136			return false;
1137	}
1138	return false;
1139}
1140
1141static void FoldPerlDoc(unsigned int startPos, int length, int, WordList *[],
1142                            Accessor &styler) {
1143	bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
1144	bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
1145	// Custom folding of POD and packages
1146	bool foldPOD = styler.GetPropertyInt("fold.perl.pod", 1) != 0;
1147	bool foldPackage = styler.GetPropertyInt("fold.perl.package", 1) != 0;
1148	unsigned int endPos = startPos + length;
1149	int visibleChars = 0;
1150	int lineCurrent = styler.GetLine(startPos);
1151	int levelPrev = SC_FOLDLEVELBASE;
1152	if (lineCurrent > 0)
1153		levelPrev = styler.LevelAt(lineCurrent - 1) >> 16;
1154	int levelCurrent = levelPrev;
1155	char chNext = styler[startPos];
1156	char chPrev = styler.SafeGetCharAt(startPos - 1);
1157	int styleNext = styler.StyleAt(startPos);
1158	// Used at end of line to determine if the line was a package definition
1159	bool isPackageLine = false;
1160	bool isPodHeading = false;
1161	for (unsigned int i = startPos; i < endPos; i++) {
1162		char ch = chNext;
1163		chNext = styler.SafeGetCharAt(i + 1);
1164		int style = styleNext;
1165		styleNext = styler.StyleAt(i + 1);
1166		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
1167		bool atLineStart = isEOLChar(chPrev) || i == 0;
1168        // Comment folding
1169		if (foldComment && atEOL && IsCommentLine(lineCurrent, styler))
1170        {
1171            if (!IsCommentLine(lineCurrent - 1, styler)
1172                && IsCommentLine(lineCurrent + 1, styler))
1173                levelCurrent++;
1174            else if (IsCommentLine(lineCurrent - 1, styler)
1175                     && !IsCommentLine(lineCurrent+1, styler))
1176                levelCurrent--;
1177        }
1178		if (style == SCE_C_OPERATOR) {
1179			if (ch == '{') {
1180				levelCurrent++;
1181			} else if (ch == '}') {
1182				levelCurrent--;
1183			}
1184		}
1185		// Custom POD folding
1186		if (foldPOD && atLineStart) {
1187			int stylePrevCh = (i) ? styler.StyleAt(i - 1):SCE_PL_DEFAULT;
1188			if (style == SCE_PL_POD) {
1189				if (stylePrevCh != SCE_PL_POD && stylePrevCh != SCE_PL_POD_VERB)
1190					levelCurrent++;
1191				else if (styler.Match(i, "=cut"))
1192					levelCurrent--;
1193				else if (styler.Match(i, "=head"))
1194					isPodHeading = true;
1195			} else if (style == SCE_PL_DATASECTION) {
1196                if (ch == '=' && isalpha(chNext) && levelCurrent == SC_FOLDLEVELBASE)
1197                    levelCurrent++;
1198                else if (styler.Match(i, "=cut") && levelCurrent > SC_FOLDLEVELBASE)
1199                    levelCurrent--;
1200                else if (styler.Match(i, "=head"))
1201					isPodHeading = true;
1202                // if package used or unclosed brace, level > SC_FOLDLEVELBASE!
1203                // reset needed as level test is vs. SC_FOLDLEVELBASE
1204                else if (styler.Match(i, "__END__"))
1205                    levelCurrent = SC_FOLDLEVELBASE;
1206            }
1207		}
1208		// Custom package folding
1209		if (foldPackage && atLineStart) {
1210			if (style == SCE_PL_WORD && styler.Match(i, "package")) {
1211				isPackageLine = true;
1212			}
1213		}
1214
1215		if (atEOL) {
1216			int lev = levelPrev;
1217			if (isPodHeading) {
1218                lev = levelPrev - 1;
1219                lev |= SC_FOLDLEVELHEADERFLAG;
1220                isPodHeading = false;
1221			}
1222			// Check if line was a package declaration
1223			// because packages need "special" treatment
1224			if (isPackageLine) {
1225				lev = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG;
1226				levelCurrent = SC_FOLDLEVELBASE + 1;
1227				isPackageLine = false;
1228			}
1229            lev |= levelCurrent << 16;
1230			if (visibleChars == 0 && foldCompact)
1231				lev |= SC_FOLDLEVELWHITEFLAG;
1232			if ((levelCurrent > levelPrev) && (visibleChars > 0))
1233				lev |= SC_FOLDLEVELHEADERFLAG;
1234			if (lev != styler.LevelAt(lineCurrent)) {
1235				styler.SetLevel(lineCurrent, lev);
1236			}
1237			lineCurrent++;
1238			levelPrev = levelCurrent;
1239			visibleChars = 0;
1240		}
1241		if (!isspacechar(ch))
1242			visibleChars++;
1243		chPrev = ch;
1244	}
1245	// Fill in the real level of the next line, keeping the current flags as they will be filled in later
1246	int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
1247	styler.SetLevel(lineCurrent, levelPrev | flagsNext);
1248}
1249
1250static const char * const perlWordListDesc[] = {
1251	"Ke