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