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