1 // Scintilla source code edit control
2 /** @file LexClw.cxx
3  ** Lexer for Clarion.
4  ** 2004/12/17 Updated Lexer
5  **/
6 // Copyright 2003-2004 by Ron Schofield <ron@schofieldcomputer.com>
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 <stdio.h>
12 #include <stdarg.h>
13 #include <assert.h>
14 #include <ctype.h>
15 
16 #include "ILexer.h"
17 #include "Scintilla.h"
18 #include "SciLexer.h"
19 
20 #include "WordList.h"
21 #include "LexAccessor.h"
22 #include "Accessor.h"
23 #include "StyleContext.h"
24 #include "CharacterSet.h"
25 #include "LexerModule.h"
26 
27 #ifdef SCI_NAMESPACE
28 using namespace Scintilla;
29 #endif
30 
31 // Is an end of line character
IsEOL(const int ch)32 inline bool IsEOL(const int ch) {
33 
34 	return(ch == '\n');
35 }
36 
37 // Convert character to uppercase
CharacterUpper(char chChar)38 static char CharacterUpper(char chChar) {
39 
40 	if (chChar < 'a' || chChar > 'z') {
41 		return(chChar);
42 	}
43 	else {
44 		return(static_cast<char>(chChar - 'a' + 'A'));
45 	}
46 }
47 
48 // Convert string to uppercase
StringUpper(char * szString)49 static void StringUpper(char *szString) {
50 
51 	while (*szString) {
52 		*szString = CharacterUpper(*szString);
53 		szString++;
54 	}
55 }
56 
57 // Is a label start character
IsALabelStart(const int iChar)58 inline bool IsALabelStart(const int iChar) {
59 
60 	return(isalpha(iChar) || iChar == '_');
61 }
62 
63 // Is a label character
IsALabelCharacter(const int iChar)64 inline bool IsALabelCharacter(const int iChar) {
65 
66 	return(isalnum(iChar) || iChar == '_' || iChar == ':');
67 }
68 
69 // Is the character is a ! and the the next character is not a !
IsACommentStart(const int iChar)70 inline bool IsACommentStart(const int iChar) {
71 
72 	return(iChar == '!');
73 }
74 
75 // Is the character a Clarion hex character (ABCDEF)
IsAHexCharacter(const int iChar,bool bCaseSensitive)76 inline bool IsAHexCharacter(const int iChar, bool bCaseSensitive) {
77 
78 	// Case insensitive.
79 	if (!bCaseSensitive) {
80 		if (strchr("ABCDEFabcdef", iChar) != NULL) {
81 			return(true);
82 		}
83 	}
84 	// Case sensitive
85 	else {
86 		if (strchr("ABCDEF", iChar) != NULL) {
87 			return(true);
88 		}
89 	}
90 	return(false);
91 }
92 
93 // Is the character a Clarion base character (B=Binary, O=Octal, H=Hex)
IsANumericBaseCharacter(const int iChar,bool bCaseSensitive)94 inline bool IsANumericBaseCharacter(const int iChar, bool bCaseSensitive) {
95 
96 	// Case insensitive.
97 	if (!bCaseSensitive) {
98 		// If character is a numeric base character
99 		if (strchr("BOHboh", iChar) != NULL) {
100 			return(true);
101 		}
102 	}
103 	// Case sensitive
104 	else {
105 		// If character is a numeric base character
106 		if (strchr("BOH", iChar) != NULL) {
107 			return(true);
108 		}
109 	}
110 	return(false);
111 }
112 
113 // Set the correct numeric constant state
SetNumericConstantState(StyleContext & scDoc)114 inline bool SetNumericConstantState(StyleContext &scDoc) {
115 
116 	int iPoints = 0;			// Point counter
117 	char cNumericString[512];	// Numeric string buffer
118 
119 	// Buffer the current numberic string
120 	scDoc.GetCurrent(cNumericString, sizeof(cNumericString));
121 	// Loop through the string until end of string (NULL termination)
122 	for (int iIndex = 0; cNumericString[iIndex] != '\0'; iIndex++) {
123 		// Depending on the character
124 		switch (cNumericString[iIndex]) {
125 			// Is a . (point)
126 			case '.' :
127 				// Increment point counter
128 				iPoints++;
129 				break;
130 			default :
131 				break;
132 		}
133 	}
134 	// If points found (can be more than one for improper formatted number
135 	if (iPoints > 0) {
136 		return(true);
137 	}
138 	// Else no points found
139 	else {
140 		return(false);
141 	}
142 }
143 
144 // Get the next word in uppercase from the current position (keyword lookahead)
GetNextWordUpper(Accessor & styler,unsigned int uiStartPos,int iLength,char * cWord)145 inline bool GetNextWordUpper(Accessor &styler, unsigned int uiStartPos, int iLength, char *cWord) {
146 
147 	unsigned int iIndex = 0;		// Buffer Index
148 
149 	// Loop through the remaining string from the current position
150 	for (int iOffset = uiStartPos; iOffset < iLength; iOffset++) {
151 		// Get the character from the buffer using the offset
152 		char cCharacter = styler[iOffset];
153 		if (IsEOL(cCharacter)) {
154 			break;
155 		}
156 		// If the character is alphabet character
157 		if (isalpha(cCharacter)) {
158 			// Add UPPERCASE character to the word buffer
159 			cWord[iIndex++] = CharacterUpper(cCharacter);
160 		}
161 	}
162 	// Add null termination
163 	cWord[iIndex] = '\0';
164 	// If no word was found
165 	if (iIndex == 0) {
166 		// Return failure
167 		return(false);
168 	}
169 	// Else word was found
170 	else {
171 		// Return success
172 		return(true);
173 	}
174 }
175 
176 // Clarion Language Colouring Procedure
ColouriseClarionDoc(unsigned int uiStartPos,int iLength,int iInitStyle,WordList * wlKeywords[],Accessor & accStyler,bool bCaseSensitive)177 static void ColouriseClarionDoc(unsigned int uiStartPos, int iLength, int iInitStyle, WordList *wlKeywords[], Accessor &accStyler, bool bCaseSensitive) {
178 
179 	int iParenthesesLevel = 0;		// Parenthese Level
180 	int iColumn1Label = false;		// Label starts in Column 1
181 
182 	WordList &wlClarionKeywords = *wlKeywords[0];			// Clarion Keywords
183 	WordList &wlCompilerDirectives = *wlKeywords[1];		// Compiler Directives
184 	WordList &wlRuntimeExpressions = *wlKeywords[2];		// Runtime Expressions
185 	WordList &wlBuiltInProcsFuncs = *wlKeywords[3];			// Builtin Procedures and Functions
186 	WordList &wlStructsDataTypes = *wlKeywords[4];			// Structures and Data Types
187 	WordList &wlAttributes = *wlKeywords[5];				// Procedure Attributes
188 	WordList &wlStandardEquates = *wlKeywords[6];			// Standard Equates
189 	WordList &wlLabelReservedWords = *wlKeywords[7];		// Clarion Reserved Keywords (Labels)
190 	WordList &wlProcLabelReservedWords = *wlKeywords[8];	// Clarion Reserved Keywords (Procedure Labels)
191 
192 	const char wlProcReservedKeywordList[] =
193 	"PROCEDURE FUNCTION";
194 	WordList wlProcReservedKeywords;
195 	wlProcReservedKeywords.Set(wlProcReservedKeywordList);
196 
197 	const char wlCompilerKeywordList[] =
198 	"COMPILE OMIT";
199 	WordList wlCompilerKeywords;
200 	wlCompilerKeywords.Set(wlCompilerKeywordList);
201 
202 	const char wlLegacyStatementsList[] =
203 	"BOF EOF FUNCTION POINTER SHARE";
204 	WordList wlLegacyStatements;
205 	wlLegacyStatements.Set(wlLegacyStatementsList);
206 
207 	StyleContext scDoc(uiStartPos, iLength, iInitStyle, accStyler);
208 
209 	// lex source code
210     for (; scDoc.More(); scDoc.Forward())
211 	{
212 		//
213 		// Determine if the current state should terminate.
214 		//
215 
216 		// Label State Handling
217 		if (scDoc.state == SCE_CLW_LABEL) {
218 			// If the character is not a valid label
219 			if (!IsALabelCharacter(scDoc.ch)) {
220 				// If the character is a . (dot syntax)
221 				if (scDoc.ch == '.') {
222 					// Turn off column 1 label flag as label now cannot be reserved work
223 					iColumn1Label = false;
224 					// Uncolour the . (dot) to default state, move forward one character,
225 					// and change back to the label state.
226 					scDoc.SetState(SCE_CLW_DEFAULT);
227 					scDoc.Forward();
228 					scDoc.SetState(SCE_CLW_LABEL);
229 				}
230 				// Else check label
231 				else {
232 					char cLabel[512];		// Label buffer
233 					// Buffer the current label string
234 					scDoc.GetCurrent(cLabel,sizeof(cLabel));
235 					// If case insensitive, convert string to UPPERCASE to match passed keywords.
236 					if (!bCaseSensitive) {
237 						StringUpper(cLabel);
238 					}
239 					// Else if UPPERCASE label string is in the Clarion compiler keyword list
240 					if (wlCompilerKeywords.InList(cLabel) && iColumn1Label){
241 						// change the label to error state
242 						scDoc.ChangeState(SCE_CLW_COMPILER_DIRECTIVE);
243 					}
244 					// Else if UPPERCASE label string is in the Clarion reserved keyword list
245 					else if (wlLabelReservedWords.InList(cLabel) && iColumn1Label){
246 						// change the label to error state
247 						scDoc.ChangeState(SCE_CLW_ERROR);
248 					}
249 					// Else if UPPERCASE label string is
250 					else if (wlProcLabelReservedWords.InList(cLabel) && iColumn1Label) {
251 						char cWord[512];	// Word buffer
252 						// Get the next word from the current position
253 						if (GetNextWordUpper(accStyler,scDoc.currentPos,uiStartPos+iLength,cWord)) {
254 							// If the next word is a procedure reserved word
255 							if (wlProcReservedKeywords.InList(cWord)) {
256 								// Change the label to error state
257 								scDoc.ChangeState(SCE_CLW_ERROR);
258 							}
259 						}
260 					}
261 					// Else if label string is in the compiler directive keyword list
262 					else if (wlCompilerDirectives.InList(cLabel)) {
263 						// change the state to compiler directive state
264 						scDoc.ChangeState(SCE_CLW_COMPILER_DIRECTIVE);
265 					}
266 					// Terminate the label state and set to default state
267 					scDoc.SetState(SCE_CLW_DEFAULT);
268 				}
269 			}
270 		}
271 		// Keyword State Handling
272 		else if (scDoc.state == SCE_CLW_KEYWORD) {
273 			// If character is : (colon)
274 			if (scDoc.ch == ':') {
275 				char cEquate[512];		// Equate buffer
276 				// Move forward to include : (colon) in buffer
277 				scDoc.Forward();
278 				// Buffer the equate string
279 				scDoc.GetCurrent(cEquate,sizeof(cEquate));
280 				// If case insensitive, convert string to UPPERCASE to match passed keywords.
281 				if (!bCaseSensitive) {
282 					StringUpper(cEquate);
283 				}
284 				// If statement string is in the equate list
285 				if (wlStandardEquates.InList(cEquate)) {
286 					// Change to equate state
287 					scDoc.ChangeState(SCE_CLW_STANDARD_EQUATE);
288 				}
289 			}
290 			// If the character is not a valid label character
291 			else if (!IsALabelCharacter(scDoc.ch)) {
292 				char cStatement[512];		// Statement buffer
293 				// Buffer the statement string
294 				scDoc.GetCurrent(cStatement,sizeof(cStatement));
295 				// If case insensitive, convert string to UPPERCASE to match passed keywords.
296 				if (!bCaseSensitive) {
297 					StringUpper(cStatement);
298 				}
299 				// If statement string is in the Clarion keyword list
300 				if (wlClarionKeywords.InList(cStatement)) {
301 					// Change the statement string to the Clarion keyword state
302 					scDoc.ChangeState(SCE_CLW_KEYWORD);
303 				}
304 				// Else if statement string is in the compiler directive keyword list
305 				else if (wlCompilerDirectives.InList(cStatement)) {
306 					// Change the statement string to the compiler directive state
307 					scDoc.ChangeState(SCE_CLW_COMPILER_DIRECTIVE);
308 				}
309 				// Else if statement string is in the runtime expressions keyword list
310 				else if (wlRuntimeExpressions.InList(cStatement)) {
311 					// Change the statement string to the runtime expressions state
312 					scDoc.ChangeState(SCE_CLW_RUNTIME_EXPRESSIONS);
313 				}
314 				// Else if statement string is in the builtin procedures and functions keyword list
315 				else if (wlBuiltInProcsFuncs.InList(cStatement)) {
316 					// Change the statement string to the builtin procedures and functions state
317 					scDoc.ChangeState(SCE_CLW_BUILTIN_PROCEDURES_FUNCTION);
318 				}
319 				// Else if statement string is in the tructures and data types keyword list
320 				else if (wlStructsDataTypes.InList(cStatement)) {
321 					// Change the statement string to the structures and data types state
322 					scDoc.ChangeState(SCE_CLW_STRUCTURE_DATA_TYPE);
323 				}
324 				// Else if statement string is in the procedure attribute keyword list
325 				else if (wlAttributes.InList(cStatement)) {
326 					// Change the statement string to the procedure attribute state
327 					scDoc.ChangeState(SCE_CLW_ATTRIBUTE);
328 				}
329 				// Else if statement string is in the standard equate keyword list
330 				else if (wlStandardEquates.InList(cStatement)) {
331 					// Change the statement string to the standard equate state
332 					scDoc.ChangeState(SCE_CLW_STANDARD_EQUATE);
333 				}
334 				// Else if statement string is in the deprecated or legacy keyword list
335 				else if (wlLegacyStatements.InList(cStatement)) {
336 					// Change the statement string to the standard equate state
337 					scDoc.ChangeState(SCE_CLW_DEPRECATED);
338 				}
339 				// Else the statement string doesn't match any work list
340 				else {
341 					// Change the statement string to the default state
342 					scDoc.ChangeState(SCE_CLW_DEFAULT);
343 				}
344 				// Terminate the keyword state and set to default state
345 				scDoc.SetState(SCE_CLW_DEFAULT);
346 			}
347 		}
348 		// String State Handling
349 		else if (scDoc.state == SCE_CLW_STRING) {
350 			// If the character is an ' (single quote)
351 			if (scDoc.ch == '\'') {
352 				// Set the state to default and move forward colouring
353 				// the ' (single quote) as default state
354 				// terminating the string state
355 				scDoc.SetState(SCE_CLW_DEFAULT);
356 				scDoc.Forward();
357 			}
358 			// If the next character is an ' (single quote)
359 			if (scDoc.chNext == '\'') {
360 				// Move forward one character and set to default state
361 				// colouring the next ' (single quote) as default state
362 				// terminating the string state
363 				scDoc.ForwardSetState(SCE_CLW_DEFAULT);
364 				scDoc.Forward();
365 			}
366 		}
367 		// Picture String State Handling
368 		else if (scDoc.state == SCE_CLW_PICTURE_STRING) {
369 			// If the character is an ( (open parenthese)
370 			if (scDoc.ch == '(') {
371 				// Increment the parenthese level
372 				iParenthesesLevel++;
373 			}
374 			// Else if the character is a ) (close parenthese)
375 			else if (scDoc.ch == ')') {
376 				// If the parenthese level is set to zero
377 				// parentheses matched
378 				if (!iParenthesesLevel) {
379 					scDoc.SetState(SCE_CLW_DEFAULT);
380 				}
381 				// Else parenthese level is greater than zero
382 				// still looking for matching parentheses
383 				else {
384 					// Decrement the parenthese level
385 					iParenthesesLevel--;
386 				}
387 			}
388 		}
389 		// Standard Equate State Handling
390 		else if (scDoc.state == SCE_CLW_STANDARD_EQUATE) {
391 			if (!isalnum(scDoc.ch)) {
392 				scDoc.SetState(SCE_CLW_DEFAULT);
393 			}
394 		}
395 		// Integer Constant State Handling
396 		else if (scDoc.state == SCE_CLW_INTEGER_CONSTANT) {
397 			// If the character is not a digit (0-9)
398 			// or character is not a hexidecimal character (A-F)
399 			// or character is not a . (point)
400 			// or character is not a numberic base character (B,O,H)
401 			if (!(isdigit(scDoc.ch)
402 			|| IsAHexCharacter(scDoc.ch, bCaseSensitive)
403 			|| scDoc.ch == '.'
404 			|| IsANumericBaseCharacter(scDoc.ch, bCaseSensitive))) {
405 				// If the number was a real
406 				if (SetNumericConstantState(scDoc)) {
407 					// Colour the matched string to the real constant state
408 					scDoc.ChangeState(SCE_CLW_REAL_CONSTANT);
409 				}
410 				// Else the number was an integer
411 				else {
412 					// Colour the matched string to an integer constant state
413 					scDoc.ChangeState(SCE_CLW_INTEGER_CONSTANT);
414 				}
415 				// Terminate the integer constant state and set to default state
416 				scDoc.SetState(SCE_CLW_DEFAULT);
417 			}
418 		}
419 
420 		//
421 		// Determine if a new state should be entered.
422 		//
423 
424 		// Beginning of Line Handling
425 		if (scDoc.atLineStart) {
426 			// Reset the column 1 label flag
427 			iColumn1Label = false;
428 			// If column 1 character is a label start character
429 			if (IsALabelStart(scDoc.ch)) {
430 				// Label character is found in column 1
431 				// so set column 1 label flag and clear last column 1 label
432 				iColumn1Label = true;
433 				// Set the state to label
434 				scDoc.SetState(SCE_CLW_LABEL);
435 			}
436 			// else if character is a space or tab
437 			else if (IsASpace(scDoc.ch)){
438 				// Set to default state
439 				scDoc.SetState(SCE_CLW_DEFAULT);
440 			}
441 			// else if comment start (!) or is an * (asterisk)
442 			else if (IsACommentStart(scDoc.ch) || scDoc.ch == '*' ) {
443 				// then set the state to comment.
444 				scDoc.SetState(SCE_CLW_COMMENT);
445 			}
446 			// else the character is a ? (question mark)
447 			else if (scDoc.ch == '?') {
448 				// Change to the compiler directive state, move forward,
449 				// colouring the ? (question mark), change back to default state.
450 				scDoc.ChangeState(SCE_CLW_COMPILER_DIRECTIVE);
451 				scDoc.Forward();
452 				scDoc.SetState(SCE_CLW_DEFAULT);
453 			}
454 			// else an invalid character in column 1
455 			else {
456 				// Set to error state
457 				scDoc.SetState(SCE_CLW_ERROR);
458 			}
459 		}
460 		// End of Line Handling
461 		else if (scDoc.atLineEnd) {
462 			// Reset to the default state at the end of each line.
463 			scDoc.SetState(SCE_CLW_DEFAULT);
464 		}
465 		// Default Handling
466 		else {
467 			// If in default state
468 			if (scDoc.state == SCE_CLW_DEFAULT) {
469 				// If is a letter could be a possible statement
470 				if (isalpha(scDoc.ch)) {
471 					// Set the state to Clarion Keyword and verify later
472 					scDoc.SetState(SCE_CLW_KEYWORD);
473 				}
474 				// else is a number
475 				else if (isdigit(scDoc.ch)) {
476 					// Set the state to Integer Constant and verify later
477 					scDoc.SetState(SCE_CLW_INTEGER_CONSTANT);
478 				}
479 				// else if the start of a comment or a | (line continuation)
480 				else if (IsACommentStart(scDoc.ch) || scDoc.ch == '|') {
481 					// then set the state to comment.
482 					scDoc.SetState(SCE_CLW_COMMENT);
483 				}
484 				// else if the character is a ' (single quote)
485 				else if (scDoc.ch == '\'') {
486 					// If the character is also a ' (single quote)
487 					// Embedded Apostrophe
488 					if (scDoc.chNext == '\'') {
489 						// Move forward colouring it as default state
490 						scDoc.ForwardSetState(SCE_CLW_DEFAULT);
491 					}
492 					else {
493 						// move to the next character and then set the state to comment.
494 						scDoc.ForwardSetState(SCE_CLW_STRING);
495 					}
496 				}
497 				// else the character is an @ (ampersand)
498 				else if (scDoc.ch == '@') {
499 					// Case insensitive.
500 					if (!bCaseSensitive) {
501 						// If character is a valid picture token character
502 						if (strchr("DEKNPSTdeknpst", scDoc.chNext) != NULL) {
503 							// Set to the picture string state
504 							scDoc.SetState(SCE_CLW_PICTURE_STRING);
505 						}
506 					}
507 					// Case sensitive
508 					else {
509 						// If character is a valid picture token character
510 						if (strchr("DEKNPST", scDoc.chNext) != NULL) {
511 							// Set the picture string state
512 							scDoc.SetState(SCE_CLW_PICTURE_STRING);
513 						}
514 					}
515 				}
516 			}
517 		}
518 	}
519 	// lexing complete
520 	scDoc.Complete();
521 }
522 
523 // Clarion Language Case Sensitive Colouring Procedure
ColouriseClarionDocSensitive(unsigned int uiStartPos,int iLength,int iInitStyle,WordList * wlKeywords[],Accessor & accStyler)524 static void ColouriseClarionDocSensitive(unsigned int uiStartPos, int iLength, int iInitStyle, WordList *wlKeywords[], Accessor &accStyler) {
525 
526 	ColouriseClarionDoc(uiStartPos, iLength, iInitStyle, wlKeywords, accStyler, true);
527 }
528 
529 // Clarion Language Case Insensitive Colouring Procedure
ColouriseClarionDocInsensitive(unsigned int uiStartPos,int iLength,int iInitStyle,WordList * wlKeywords[],Accessor & accStyler)530 static void ColouriseClarionDocInsensitive(unsigned int uiStartPos, int iLength, int iInitStyle, WordList *wlKeywords[], Accessor &accStyler) {
531 
532 	ColouriseClarionDoc(uiStartPos, iLength, iInitStyle, wlKeywords, accStyler, false);
533 }
534 
535 // Fill Buffer
536 
FillBuffer(unsigned int uiStart,unsigned int uiEnd,Accessor & accStyler,char * szBuffer,unsigned int uiLength)537 static void FillBuffer(unsigned int uiStart, unsigned int uiEnd, Accessor &accStyler, char *szBuffer, unsigned int uiLength) {
538 
539 	unsigned int uiPos = 0;
540 
541 	while ((uiPos < uiEnd - uiStart + 1) && (uiPos < uiLength-1)) {
542 		szBuffer[uiPos] = static_cast<char>(toupper(accStyler[uiStart + uiPos]));
543 		uiPos++;
544 	}
545 	szBuffer[uiPos] = '\0';
546 }
547 
548 // Classify Clarion Fold Point
549 
ClassifyClarionFoldPoint(int iLevel,const char * szString)550 static int ClassifyClarionFoldPoint(int iLevel, const char* szString) {
551 
552 	if (!(isdigit(szString[0]) || (szString[0] == '.'))) {
553 		if (strcmp(szString, "PROCEDURE") == 0) {
554 	//		iLevel = SC_FOLDLEVELBASE + 1;
555 		}
556 		else if (strcmp(szString, "MAP") == 0 ||
557 			strcmp(szString,"ACCEPT") == 0 ||
558 			strcmp(szString,"BEGIN") == 0 ||
559 			strcmp(szString,"CASE") == 0 ||
560 			strcmp(szString,"EXECUTE") == 0 ||
561 			strcmp(szString,"IF") == 0 ||
562 			strcmp(szString,"ITEMIZE") == 0 ||
563 			strcmp(szString,"INTERFACE") == 0 ||
564 			strcmp(szString,"JOIN") == 0 ||
565 			strcmp(szString,"LOOP") == 0 ||
566 			strcmp(szString,"MODULE") == 0 ||
567 			strcmp(szString,"RECORD") == 0) {
568 			iLevel++;
569 		}
570 		else if (strcmp(szString, "APPLICATION") == 0 ||
571 			strcmp(szString, "CLASS") == 0 ||
572 			strcmp(szString, "DETAIL") == 0 ||
573 			strcmp(szString, "FILE") == 0 ||
574 			strcmp(szString, "FOOTER") == 0 ||
575 			strcmp(szString, "FORM") == 0 ||
576 			strcmp(szString, "GROUP") == 0 ||
577 			strcmp(szString, "HEADER") == 0 ||
578 			strcmp(szString, "INTERFACE") == 0 ||
579 			strcmp(szString, "MENU") == 0 ||
580 			strcmp(szString, "MENUBAR") == 0 ||
581 			strcmp(szString, "OLE") == 0 ||
582 			strcmp(szString, "OPTION") == 0 ||
583 			strcmp(szString, "QUEUE") == 0 ||
584 			strcmp(szString, "REPORT") == 0 ||
585 			strcmp(szString, "SHEET") == 0 ||
586 			strcmp(szString, "TAB") == 0 ||
587 			strcmp(szString, "TOOLBAR") == 0 ||
588 			strcmp(szString, "VIEW") == 0 ||
589 			strcmp(szString, "WINDOW") == 0) {
590 			iLevel++;
591 		}
592 		else if (strcmp(szString, "END") == 0 ||
593 			strcmp(szString, "UNTIL") == 0 ||
594 			strcmp(szString, "WHILE") == 0) {
595 			iLevel--;
596 		}
597 	}
598 	return(iLevel);
599 }
600 
601 // Clarion Language Folding Procedure
FoldClarionDoc(unsigned int uiStartPos,int iLength,int iInitStyle,WordList * [],Accessor & accStyler)602 static void FoldClarionDoc(unsigned int uiStartPos, int iLength, int iInitStyle, WordList *[], Accessor &accStyler) {
603 
604 	unsigned int uiEndPos = uiStartPos + iLength;
605 	int iLineCurrent = accStyler.GetLine(uiStartPos);
606 	int iLevelPrev = accStyler.LevelAt(iLineCurrent) & SC_FOLDLEVELNUMBERMASK;
607 	int iLevelCurrent = iLevelPrev;
608 	char chNext = accStyler[uiStartPos];
609 	int iStyle = iInitStyle;
610 	int iStyleNext = accStyler.StyleAt(uiStartPos);
611 	int iVisibleChars = 0;
612 	int iLastStart = 0;
613 
614 	for (unsigned int uiPos = uiStartPos; uiPos < uiEndPos; uiPos++) {
615 
616 		char chChar = chNext;
617 		chNext = accStyler.SafeGetCharAt(uiPos + 1);
618 		int iStylePrev = iStyle;
619 		iStyle = iStyleNext;
620 		iStyleNext = accStyler.StyleAt(uiPos + 1);
621 		bool bEOL = (chChar == '\r' && chNext != '\n') || (chChar == '\n');
622 
623 		if (iStylePrev == SCE_CLW_DEFAULT) {
624 			if (iStyle == SCE_CLW_KEYWORD || iStyle == SCE_CLW_STRUCTURE_DATA_TYPE) {
625 				// Store last word start point.
626 				iLastStart = uiPos;
627 			}
628 		}
629 
630 		if (iStylePrev == SCE_CLW_KEYWORD || iStylePrev == SCE_CLW_STRUCTURE_DATA_TYPE) {
631 			if(iswordchar(chChar) && !iswordchar(chNext)) {
632 				char chBuffer[100];
633 				FillBuffer(iLastStart, uiPos, accStyler, chBuffer, sizeof(chBuffer));
634 				iLevelCurrent = ClassifyClarionFoldPoint(iLevelCurrent,chBuffer);
635 			//	if ((iLevelCurrent == SC_FOLDLEVELBASE + 1) && iLineCurrent > 1) {
636 			//		accStyler.SetLevel(iLineCurrent-1,SC_FOLDLEVELBASE);
637 			//		iLevelPrev = SC_FOLDLEVELBASE;
638 			//	}
639 			}
640 		}
641 
642 		if (bEOL) {
643 			int iLevel = iLevelPrev;
644 			if ((iLevelCurrent > iLevelPrev) && (iVisibleChars > 0))
645 				iLevel |= SC_FOLDLEVELHEADERFLAG;
646 			if (iLevel != accStyler.LevelAt(iLineCurrent)) {
647 				accStyler.SetLevel(iLineCurrent,iLevel);
648 			}
649 			iLineCurrent++;
650 			iLevelPrev = iLevelCurrent;
651 			iVisibleChars = 0;
652 		}
653 
654 		if (!isspacechar(chChar))
655 			iVisibleChars++;
656 	}
657 
658 	// Fill in the real level of the next line, keeping the current flags
659 	// as they will be filled in later.
660 	int iFlagsNext = accStyler.LevelAt(iLineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
661 	accStyler.SetLevel(iLineCurrent, iLevelPrev | iFlagsNext);
662 }
663 
664 // Word List Descriptions
665 static const char * const rgWordListDescriptions[] = {
666 	"Clarion Keywords",
667 	"Compiler Directives",
668 	"Built-in Procedures and Functions",
669 	"Runtime Expressions",
670 	"Structure and Data Types",
671 	"Attributes",
672 	"Standard Equates",
673 	"Reserved Words (Labels)",
674 	"Reserved Words (Procedure Labels)",
675 	0,
676 };
677 
678 // Case Sensitive Clarion Language Lexer
679 LexerModule lmClw(SCLEX_CLW, ColouriseClarionDocSensitive, "clarion", FoldClarionDoc, rgWordListDescriptions);
680 
681 // Case Insensitive Clarion Language Lexer
682 LexerModule lmClwNoCase(SCLEX_CLWNOCASE, ColouriseClarionDocInsensitive, "clarionnocase", FoldClarionDoc, rgWordListDescriptions);
683