1 // Scintilla source code edit control
4 ** 2004/12/17 Updated Lexer
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.
19 #include "StyleContext.h"
21 #include "Scintilla.h"
25 using namespace Scintilla
;
28 // Is an end of line character
29 inline bool IsEOL(const int ch
) {
34 // Convert character to uppercase
35 static char CharacterUpper(char chChar
) {
37 if (chChar
< 'a' || chChar
> 'z') {
41 return(static_cast<char>(chChar
- 'a' + 'A'));
45 // Convert string to uppercase
46 static void StringUpper(char *szString
) {
49 *szString
= CharacterUpper(*szString
);
54 // Is a label start character
55 inline bool IsALabelStart(const int iChar
) {
57 return(isalpha(iChar
) || iChar
== '_');
60 // Is a label character
61 inline bool IsALabelCharacter(const int iChar
) {
63 return(isalnum(iChar
) || iChar
== '_' || iChar
== ':');
66 // Is the character is a ! and the the next character is not a !
67 inline bool IsACommentStart(const int iChar
) {
72 // Is the character a Clarion hex character (ABCDEF)
73 inline bool IsAHexCharacter(const int iChar
, bool bCaseSensitive
) {
76 if (!bCaseSensitive
) {
77 if (strchr("ABCDEFabcdef", iChar
) != NULL
) {
83 if (strchr("ABCDEF", iChar
) != NULL
) {
90 // Is the character a Clarion base character (B=Binary, O=Octal, H=Hex)
91 inline bool IsANumericBaseCharacter(const int iChar
, bool bCaseSensitive
) {
94 if (!bCaseSensitive
) {
95 // If character is a numeric base character
96 if (strchr("BOHboh", iChar
) != NULL
) {
102 // If character is a numeric base character
103 if (strchr("BOH", iChar
) != NULL
) {
110 // Set the correct numeric constant state
111 inline bool SetNumericConstantState(StyleContext
&scDoc
) {
113 int iPoints
= 0; // Point counter
114 char cNumericString
[512]; // Numeric string buffer
116 // Buffer the current numberic string
117 scDoc
.GetCurrent(cNumericString
, sizeof(cNumericString
));
118 // Loop through the string until end of string (NULL termination)
119 for (int iIndex
= 0; cNumericString
[iIndex
] != '\0'; iIndex
++) {
120 // Depending on the character
121 switch (cNumericString
[iIndex
]) {
124 // Increment point counter
131 // If points found (can be more than one for improper formatted number
135 // Else no points found
141 // Get the next word in uppercase from the current position (keyword lookahead)
142 inline bool GetNextWordUpper(Accessor
&styler
, unsigned int uiStartPos
, int iLength
, char *cWord
) {
144 unsigned int iIndex
= 0; // Buffer Index
146 // Loop through the remaining string from the current position
147 for (int iOffset
= uiStartPos
; iOffset
< iLength
; iOffset
++) {
148 // Get the character from the buffer using the offset
149 char cCharacter
= styler
[iOffset
];
150 if (IsEOL(cCharacter
)) {
153 // If the character is alphabet character
154 if (isalpha(cCharacter
)) {
155 // Add UPPERCASE character to the word buffer
156 cWord
[iIndex
++] = CharacterUpper(cCharacter
);
159 // Add null termination
160 cWord
[iIndex
] = '\0';
161 // If no word was found
166 // Else word was found
173 // Clarion Language Colouring Procedure
174 static void ColouriseClarionDoc(unsigned int uiStartPos
, int iLength
, int iInitStyle
, WordList
*wlKeywords
[], Accessor
&accStyler
, bool bCaseSensitive
) {
176 int iParenthesesLevel
= 0; // Parenthese Level
177 int iColumn1Label
= false; // Label starts in Column 1
179 WordList
&wlClarionKeywords
= *wlKeywords
[0]; // Clarion Keywords
180 WordList
&wlCompilerDirectives
= *wlKeywords
[1]; // Compiler Directives
181 WordList
&wlRuntimeExpressions
= *wlKeywords
[2]; // Runtime Expressions
182 WordList
&wlBuiltInProcsFuncs
= *wlKeywords
[3]; // Builtin Procedures and Functions
183 WordList
&wlStructsDataTypes
= *wlKeywords
[4]; // Structures and Data Types
184 WordList
&wlAttributes
= *wlKeywords
[5]; // Procedure Attributes
185 WordList
&wlStandardEquates
= *wlKeywords
[6]; // Standard Equates
186 WordList
&wlLabelReservedWords
= *wlKeywords
[7]; // Clarion Reserved Keywords (Labels)
187 WordList
&wlProcLabelReservedWords
= *wlKeywords
[8]; // Clarion Reserved Keywords (Procedure Labels)
189 const char wlProcReservedKeywordList
[] =
190 "PROCEDURE FUNCTION";
191 WordList wlProcReservedKeywords
;
192 wlProcReservedKeywords
.Set(wlProcReservedKeywordList
);
194 const char wlCompilerKeywordList
[] =
196 WordList wlCompilerKeywords
;
197 wlCompilerKeywords
.Set(wlCompilerKeywordList
);
199 const char wlLegacyStatementsList
[] =
200 "BOF EOF FUNCTION POINTER SHARE";
201 WordList wlLegacyStatements
;
202 wlLegacyStatements
.Set(wlLegacyStatementsList
);
204 StyleContext
scDoc(uiStartPos
, iLength
, iInitStyle
, accStyler
);
207 for (; scDoc
.More(); scDoc
.Forward())
210 // Determine if the current state should terminate.
213 // Label State Handling
214 if (scDoc
.state
== SCE_CLW_LABEL
) {
215 // If the character is not a valid label
216 if (!IsALabelCharacter(scDoc
.ch
)) {
217 // If the character is a . (dot syntax)
218 if (scDoc
.ch
== '.') {
219 // Turn off column 1 label flag as label now cannot be reserved work
220 iColumn1Label
= false;
221 // Uncolour the . (dot) to default state, move forward one character,
222 // and change back to the label state.
223 scDoc
.SetState(SCE_CLW_DEFAULT
);
225 scDoc
.SetState(SCE_CLW_LABEL
);
229 char cLabel
[512]; // Label buffer
230 // Buffer the current label string
231 scDoc
.GetCurrent(cLabel
,sizeof(cLabel
));
232 // If case insensitive, convert string to UPPERCASE to match passed keywords.
233 if (!bCaseSensitive
) {
236 // Else if UPPERCASE label string is in the Clarion compiler keyword list
237 if (wlCompilerKeywords
.InList(cLabel
) && iColumn1Label
){
238 // change the label to error state
239 scDoc
.ChangeState(SCE_CLW_COMPILER_DIRECTIVE
);
241 // Else if UPPERCASE label string is in the Clarion reserved keyword list
242 else if (wlLabelReservedWords
.InList(cLabel
) && iColumn1Label
){
243 // change the label to error state
244 scDoc
.ChangeState(SCE_CLW_ERROR
);
246 // Else if UPPERCASE label string is
247 else if (wlProcLabelReservedWords
.InList(cLabel
) && iColumn1Label
) {
248 char cWord
[512]; // Word buffer
249 // Get the next word from the current position
250 if (GetNextWordUpper(accStyler
,scDoc
.currentPos
,uiStartPos
+iLength
,cWord
)) {
251 // If the next word is a procedure reserved word
252 if (wlProcReservedKeywords
.InList(cWord
)) {
253 // Change the label to error state
254 scDoc
.ChangeState(SCE_CLW_ERROR
);
258 // Else if label string is in the compiler directive keyword list
259 else if (wlCompilerDirectives
.InList(cLabel
)) {
260 // change the state to compiler directive state
261 scDoc
.ChangeState(SCE_CLW_COMPILER_DIRECTIVE
);
263 // Terminate the label state and set to default state
264 scDoc
.SetState(SCE_CLW_DEFAULT
);
268 // Keyword State Handling
269 else if (scDoc
.state
== SCE_CLW_KEYWORD
) {
270 // If character is : (colon)
271 if (scDoc
.ch
== ':') {
272 char cEquate
[512]; // Equate buffer
273 // Move forward to include : (colon) in buffer
275 // Buffer the equate string
276 scDoc
.GetCurrent(cEquate
,sizeof(cEquate
));
277 // If case insensitive, convert string to UPPERCASE to match passed keywords.
278 if (!bCaseSensitive
) {
279 StringUpper(cEquate
);
281 // If statement string is in the equate list
282 if (wlStandardEquates
.InList(cEquate
)) {
283 // Change to equate state
284 scDoc
.ChangeState(SCE_CLW_STANDARD_EQUATE
);
287 // If the character is not a valid label character
288 else if (!IsALabelCharacter(scDoc
.ch
)) {
289 char cStatement
[512]; // Statement buffer
290 // Buffer the statement string
291 scDoc
.GetCurrent(cStatement
,sizeof(cStatement
));
292 // If case insensitive, convert string to UPPERCASE to match passed keywords.
293 if (!bCaseSensitive
) {
294 StringUpper(cStatement
);
296 // If statement string is in the Clarion keyword list
297 if (wlClarionKeywords
.InList(cStatement
)) {
298 // Change the statement string to the Clarion keyword state
299 scDoc
.ChangeState(SCE_CLW_KEYWORD
);
301 // Else if statement string is in the compiler directive keyword list
302 else if (wlCompilerDirectives
.InList(cStatement
)) {
303 // Change the statement string to the compiler directive state
304 scDoc
.ChangeState(SCE_CLW_COMPILER_DIRECTIVE
);
306 // Else if statement string is in the runtime expressions keyword list
307 else if (wlRuntimeExpressions
.InList(cStatement
)) {
308 // Change the statement string to the runtime expressions state
309 scDoc
.ChangeState(SCE_CLW_RUNTIME_EXPRESSIONS
);
311 // Else if statement string is in the builtin procedures and functions keyword list
312 else if (wlBuiltInProcsFuncs
.InList(cStatement
)) {
313 // Change the statement string to the builtin procedures and functions state
314 scDoc
.ChangeState(SCE_CLW_BUILTIN_PROCEDURES_FUNCTION
);
316 // Else if statement string is in the tructures and data types keyword list
317 else if (wlStructsDataTypes
.InList(cStatement
)) {
318 // Change the statement string to the structures and data types state
319 scDoc
.ChangeState(SCE_CLW_STRUCTURE_DATA_TYPE
);
321 // Else if statement string is in the procedure attribute keyword list
322 else if (wlAttributes
.InList(cStatement
)) {
323 // Change the statement string to the procedure attribute state
324 scDoc
.ChangeState(SCE_CLW_ATTRIBUTE
);
326 // Else if statement string is in the standard equate keyword list
327 else if (wlStandardEquates
.InList(cStatement
)) {
328 // Change the statement string to the standard equate state
329 scDoc
.ChangeState(SCE_CLW_STANDARD_EQUATE
);
331 // Else if statement string is in the deprecated or legacy keyword list
332 else if (wlLegacyStatements
.InList(cStatement
)) {
333 // Change the statement string to the standard equate state
334 scDoc
.ChangeState(SCE_CLW_DEPRECATED
);
336 // Else the statement string doesn't match any work list
338 // Change the statement string to the default state
339 scDoc
.ChangeState(SCE_CLW_DEFAULT
);
341 // Terminate the keyword state and set to default state
342 scDoc
.SetState(SCE_CLW_DEFAULT
);
345 // String State Handling
346 else if (scDoc
.state
== SCE_CLW_STRING
) {
347 // If the character is an ' (single quote)
348 if (scDoc
.ch
== '\'') {
349 // Set the state to default and move forward colouring
350 // the ' (single quote) as default state
351 // terminating the string state
352 scDoc
.SetState(SCE_CLW_DEFAULT
);
355 // If the next character is an ' (single quote)
356 if (scDoc
.chNext
== '\'') {
357 // Move forward one character and set to default state
358 // colouring the next ' (single quote) as default state
359 // terminating the string state
360 scDoc
.ForwardSetState(SCE_CLW_DEFAULT
);
364 // Picture String State Handling
365 else if (scDoc
.state
== SCE_CLW_PICTURE_STRING
) {
366 // If the character is an ( (open parenthese)
367 if (scDoc
.ch
== '(') {
368 // Increment the parenthese level
371 // Else if the character is a ) (close parenthese)
372 else if (scDoc
.ch
== ')') {
373 // If the parenthese level is set to zero
374 // parentheses matched
375 if (!iParenthesesLevel
) {
376 scDoc
.SetState(SCE_CLW_DEFAULT
);
378 // Else parenthese level is greater than zero
379 // still looking for matching parentheses
381 // Decrement the parenthese level
386 // Standard Equate State Handling
387 else if (scDoc
.state
== SCE_CLW_STANDARD_EQUATE
) {
388 if (!isalnum(scDoc
.ch
)) {
389 scDoc
.SetState(SCE_CLW_DEFAULT
);
392 // Integer Constant State Handling
393 else if (scDoc
.state
== SCE_CLW_INTEGER_CONSTANT
) {
394 // If the character is not a digit (0-9)
395 // or character is not a hexidecimal character (A-F)
396 // or character is not a . (point)
397 // or character is not a numberic base character (B,O,H)
398 if (!(isdigit(scDoc
.ch
)
399 || IsAHexCharacter(scDoc
.ch
, bCaseSensitive
)
401 || IsANumericBaseCharacter(scDoc
.ch
, bCaseSensitive
))) {
402 // If the number was a real
403 if (SetNumericConstantState(scDoc
)) {
404 // Colour the matched string to the real constant state
405 scDoc
.ChangeState(SCE_CLW_REAL_CONSTANT
);
407 // Else the number was an integer
409 // Colour the matched string to an integer constant state
410 scDoc
.ChangeState(SCE_CLW_INTEGER_CONSTANT
);
412 // Terminate the integer constant state and set to default state
413 scDoc
.SetState(SCE_CLW_DEFAULT
);
418 // Determine if a new state should be entered.
421 // Beginning of Line Handling
422 if (scDoc
.atLineStart
) {
423 // Reset the column 1 label flag
424 iColumn1Label
= false;
425 // If column 1 character is a label start character
426 if (IsALabelStart(scDoc
.ch
)) {
427 // Label character is found in column 1
428 // so set column 1 label flag and clear last column 1 label
429 iColumn1Label
= true;
430 // Set the state to label
431 scDoc
.SetState(SCE_CLW_LABEL
);
433 // else if character is a space or tab
434 else if (IsASpace(scDoc
.ch
)){
435 // Set to default state
436 scDoc
.SetState(SCE_CLW_DEFAULT
);
438 // else if comment start (!) or is an * (asterisk)
439 else if (IsACommentStart(scDoc
.ch
) || scDoc
.ch
== '*' ) {
440 // then set the state to comment.
441 scDoc
.SetState(SCE_CLW_COMMENT
);
443 // else the character is a ? (question mark)
444 else if (scDoc
.ch
== '?') {
445 // Change to the compiler directive state, move forward,
446 // colouring the ? (question mark), change back to default state.
447 scDoc
.ChangeState(SCE_CLW_COMPILER_DIRECTIVE
);
449 scDoc
.SetState(SCE_CLW_DEFAULT
);
451 // else an invalid character in column 1
453 // Set to error state
454 scDoc
.SetState(SCE_CLW_ERROR
);
457 // End of Line Handling
458 else if (scDoc
.atLineEnd
) {
459 // Reset to the default state at the end of each line.
460 scDoc
.SetState(SCE_CLW_DEFAULT
);
464 // If in default state
465 if (scDoc
.state
== SCE_CLW_DEFAULT
) {
466 // If is a letter could be a possible statement
467 if (isalpha(scDoc
.ch
)) {
468 // Set the state to Clarion Keyword and verify later
469 scDoc
.SetState(SCE_CLW_KEYWORD
);
472 else if (isdigit(scDoc
.ch
)) {
473 // Set the state to Integer Constant and verify later
474 scDoc
.SetState(SCE_CLW_INTEGER_CONSTANT
);
476 // else if the start of a comment or a | (line continuation)
477 else if (IsACommentStart(scDoc
.ch
) || scDoc
.ch
== '|') {
478 // then set the state to comment.
479 scDoc
.SetState(SCE_CLW_COMMENT
);
481 // else if the character is a ' (single quote)
482 else if (scDoc
.ch
== '\'') {
483 // If the character is also a ' (single quote)
484 // Embedded Apostrophe
485 if (scDoc
.chNext
== '\'') {
486 // Move forward colouring it as default state
487 scDoc
.ForwardSetState(SCE_CLW_DEFAULT
);
490 // move to the next character and then set the state to comment.
491 scDoc
.ForwardSetState(SCE_CLW_STRING
);
494 // else the character is an @ (ampersand)
495 else if (scDoc
.ch
== '@') {
497 if (!bCaseSensitive
) {
498 // If character is a valid picture token character
499 if (strchr("DEKNPSTdeknpst", scDoc
.chNext
) != NULL
) {
500 // Set to the picture string state
501 scDoc
.SetState(SCE_CLW_PICTURE_STRING
);
506 // If character is a valid picture token character
507 if (strchr("DEKNPST", scDoc
.chNext
) != NULL
) {
508 // Set the picture string state
509 scDoc
.SetState(SCE_CLW_PICTURE_STRING
);
520 // Clarion Language Case Sensitive Colouring Procedure
521 static void ColouriseClarionDocSensitive(unsigned int uiStartPos
, int iLength
, int iInitStyle
, WordList
*wlKeywords
[], Accessor
&accStyler
) {
523 ColouriseClarionDoc(uiStartPos
, iLength
, iInitStyle
, wlKeywords
, accStyler
, true);
526 // Clarion Language Case Insensitive Colouring Procedure
527 static void ColouriseClarionDocInsensitive(unsigned int uiStartPos
, int iLength
, int iInitStyle
, WordList
*wlKeywords
[], Accessor
&accStyler
) {
529 ColouriseClarionDoc(uiStartPos
, iLength
, iInitStyle
, wlKeywords
, accStyler
, false);
534 static void FillBuffer(unsigned int uiStart
, unsigned int uiEnd
, Accessor
&accStyler
, char *szBuffer
, unsigned int uiLength
) {
536 unsigned int uiPos
= 0;
538 while ((uiPos
< uiEnd
- uiStart
+ 1) && (uiPos
< uiLength
-1)) {
539 szBuffer
[uiPos
] = static_cast<char>(toupper(accStyler
[uiStart
+ uiPos
]));
542 szBuffer
[uiPos
] = '\0';
545 // Classify Clarion Fold Point
547 static int ClassifyClarionFoldPoint(int iLevel
, const char* szString
) {
549 if (!(isdigit(szString
[0]) || (szString
[0] == '.'))) {
550 if (strcmp(szString
, "PROCEDURE") == 0) {
551 // iLevel = SC_FOLDLEVELBASE + 1;
553 else if (strcmp(szString
, "MAP") == 0 ||
554 strcmp(szString
,"ACCEPT") == 0 ||
555 strcmp(szString
,"BEGIN") == 0 ||
556 strcmp(szString
,"CASE") == 0 ||
557 strcmp(szString
,"EXECUTE") == 0 ||
558 strcmp(szString
,"IF") == 0 ||
559 strcmp(szString
,"ITEMIZE") == 0 ||
560 strcmp(szString
,"INTERFACE") == 0 ||
561 strcmp(szString
,"JOIN") == 0 ||
562 strcmp(szString
,"LOOP") == 0 ||
563 strcmp(szString
,"MODULE") == 0 ||
564 strcmp(szString
,"RECORD") == 0) {
567 else if (strcmp(szString
, "APPLICATION") == 0 ||
568 strcmp(szString
, "CLASS") == 0 ||
569 strcmp(szString
, "DETAIL") == 0 ||
570 strcmp(szString
, "FILE") == 0 ||
571 strcmp(szString
, "FOOTER") == 0 ||
572 strcmp(szString
, "FORM") == 0 ||
573 strcmp(szString
, "GROUP") == 0 ||
574 strcmp(szString
, "HEADER") == 0 ||
575 strcmp(szString
, "INTERFACE") == 0 ||
576 strcmp(szString
, "MENU") == 0 ||
577 strcmp(szString
, "MENUBAR") == 0 ||
578 strcmp(szString
, "OLE") == 0 ||
579 strcmp(szString
, "OPTION") == 0 ||
580 strcmp(szString
, "QUEUE") == 0 ||
581 strcmp(szString
, "REPORT") == 0 ||
582 strcmp(szString
, "SHEET") == 0 ||
583 strcmp(szString
, "TAB") == 0 ||
584 strcmp(szString
, "TOOLBAR") == 0 ||
585 strcmp(szString
, "VIEW") == 0 ||
586 strcmp(szString
, "WINDOW") == 0) {
589 else if (strcmp(szString
, "END") == 0 ||
590 strcmp(szString
, "UNTIL") == 0 ||
591 strcmp(szString
, "WHILE") == 0) {
598 // Clarion Language Folding Procedure
599 static void FoldClarionDoc(unsigned int uiStartPos
, int iLength
, int iInitStyle
, WordList
*[], Accessor
&accStyler
) {
601 unsigned int uiEndPos
= uiStartPos
+ iLength
;
602 int iLineCurrent
= accStyler
.GetLine(uiStartPos
);
603 int iLevelPrev
= accStyler
.LevelAt(iLineCurrent
) & SC_FOLDLEVELNUMBERMASK
;
604 int iLevelCurrent
= iLevelPrev
;
605 char chNext
= accStyler
[uiStartPos
];
606 int iStyle
= iInitStyle
;
607 int iStyleNext
= accStyler
.StyleAt(uiStartPos
);
608 int iVisibleChars
= 0;
611 for (unsigned int uiPos
= uiStartPos
; uiPos
< uiEndPos
; uiPos
++) {
613 char chChar
= chNext
;
614 chNext
= accStyler
.SafeGetCharAt(uiPos
+ 1);
615 int iStylePrev
= iStyle
;
617 iStyleNext
= accStyler
.StyleAt(uiPos
+ 1);
618 bool bEOL
= (chChar
== '\r' && chNext
!= '\n') || (chChar
== '\n');
620 if (iStylePrev
== SCE_CLW_DEFAULT
) {
621 if (iStyle
== SCE_CLW_KEYWORD
|| iStyle
== SCE_CLW_STRUCTURE_DATA_TYPE
) {
622 // Store last word start point.
627 if (iStylePrev
== SCE_CLW_KEYWORD
|| iStylePrev
== SCE_CLW_STRUCTURE_DATA_TYPE
) {
628 if(iswordchar(chChar
) && !iswordchar(chNext
)) {
630 FillBuffer(iLastStart
, uiPos
, accStyler
, chBuffer
, sizeof(chBuffer
));
631 iLevelCurrent
= ClassifyClarionFoldPoint(iLevelCurrent
,chBuffer
);
632 // if ((iLevelCurrent == SC_FOLDLEVELBASE + 1) && iLineCurrent > 1) {
633 // accStyler.SetLevel(iLineCurrent-1,SC_FOLDLEVELBASE);
634 // iLevelPrev = SC_FOLDLEVELBASE;
640 int iLevel
= iLevelPrev
;
641 if ((iLevelCurrent
> iLevelPrev
) && (iVisibleChars
> 0))
642 iLevel
|= SC_FOLDLEVELHEADERFLAG
;
643 if (iLevel
!= accStyler
.LevelAt(iLineCurrent
)) {
644 accStyler
.SetLevel(iLineCurrent
,iLevel
);
647 iLevelPrev
= iLevelCurrent
;
651 if (!isspacechar(chChar
))
655 // Fill in the real level of the next line, keeping the current flags
656 // as they will be filled in later.
657 int iFlagsNext
= accStyler
.LevelAt(iLineCurrent
) & ~SC_FOLDLEVELNUMBERMASK
;
658 accStyler
.SetLevel(iLineCurrent
, iLevelPrev
| iFlagsNext
);
661 // Word List Descriptions
662 static const char * const rgWordListDescriptions
[] = {
664 "Compiler Directives",
665 "Built-in Procedures and Functions",
666 "Runtime Expressions",
667 "Structure and Data Types",
670 "Reserved Words (Labels)",
671 "Reserved Words (Procedure Labels)",
675 // Case Sensitive Clarion Language Lexer
676 LexerModule
lmClw(SCLEX_CLW
, ColouriseClarionDocSensitive
, "clarion", FoldClarionDoc
, rgWordListDescriptions
);
678 // Case Insensitive Clarion Language Lexer
679 LexerModule
lmClwNoCase(SCLEX_CLWNOCASE
, ColouriseClarionDocInsensitive
, "clarionnocase", FoldClarionDoc
, rgWordListDescriptions
);