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"
24 // Is an end of line character
25 inline bool IsEOL(const int ch
) {
30 // Convert character to uppercase
31 static char CharacterUpper(char chChar
) {
33 if (chChar
< 'a' || chChar
> 'z') {
37 return(static_cast<char>(chChar
- 'a' + 'A'));
41 // Convert string to uppercase
42 static void StringUpper(char *szString
) {
45 *szString
= CharacterUpper(*szString
);
50 // Is a label start character
51 inline bool IsALabelStart(const int iChar
) {
53 return(isalpha(iChar
) || iChar
== '_');
56 // Is a label character
57 inline bool IsALabelCharacter(const int iChar
) {
59 return(isalnum(iChar
) || iChar
== '_' || iChar
== ':');
62 // Is the character is a ! and the the next character is not a !
63 inline bool IsACommentStart(const int iChar
) {
68 // Is the character a Clarion hex character (ABCDEF)
69 inline bool IsAHexCharacter(const int iChar
, bool bCaseSensitive
) {
72 if (!bCaseSensitive
) {
73 if (strchr("ABCDEFabcdef", iChar
) != NULL
) {
79 if (strchr("ABCDEF", iChar
) != NULL
) {
86 // Is the character a Clarion base character (B=Binary, O=Octal, H=Hex)
87 inline bool IsANumericBaseCharacter(const int iChar
, bool bCaseSensitive
) {
90 if (!bCaseSensitive
) {
91 // If character is a numeric base character
92 if (strchr("BOHboh", iChar
) != NULL
) {
98 // If character is a numeric base character
99 if (strchr("BOH", iChar
) != NULL
) {
106 // Set the correct numeric constant state
107 inline bool SetNumericConstantState(StyleContext
&scDoc
) {
109 int iPoints
= 0; // Point counter
110 char cNumericString
[512]; // Numeric string buffer
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
]) {
120 // Increment point counter
127 // If points found (can be more than one for improper formatted number
131 // Else no points found
137 // Get the next word in uppercase from the current position (keyword lookahead)
138 inline bool GetNextWordUpper(Accessor
&styler
, unsigned int uiStartPos
, int iLength
, char *cWord
) {
140 unsigned int iIndex
= 0; // Buffer Index
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
)) {
149 // If the character is alphabet character
150 if (isalpha(cCharacter
)) {
151 // Add UPPERCASE character to the word buffer
152 cWord
[iIndex
++] = CharacterUpper(cCharacter
);
155 // Add null termination
156 cWord
[iIndex
] = '\0';
157 // If no word was found
162 // Else word was found
169 // Clarion Language Colouring Procedure
170 static void ColouriseClarionDoc(unsigned int uiStartPos
, int iLength
, int iInitStyle
, WordList
*wlKeywords
[], Accessor
&accStyler
, bool bCaseSensitive
) {
172 int iParenthesesLevel
= 0; // Parenthese Level
173 int iColumn1Label
= false; // Label starts in Column 1
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)
185 const char wlProcReservedKeywordList
[] =
186 "PROCEDURE FUNCTION";
187 WordList wlProcReservedKeywords
;
188 wlProcReservedKeywords
.Set(wlProcReservedKeywordList
);
190 const char wlCompilerKeywordList
[] =
192 WordList wlCompilerKeywords
;
193 wlCompilerKeywords
.Set(wlCompilerKeywordList
);
195 const char wlLegacyStatementsList
[] =
196 "BOF EOF FUNCTION POINTER SHARE";
197 WordList wlLegacyStatements
;
198 wlLegacyStatements
.Set(wlLegacyStatementsList
);
200 StyleContext
scDoc(uiStartPos
, iLength
, iInitStyle
, accStyler
);
203 for (; scDoc
.More(); scDoc
.Forward())
206 // Determine if the current state should terminate.
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
);
221 scDoc
.SetState(SCE_CLW_LABEL
);
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
) {
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
);
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
);
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
);
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
);
259 // Terminate the label state and set to default state
260 scDoc
.SetState(SCE_CLW_DEFAULT
);
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
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
332 // Else the statement string doesn't match any work list
334 // Change the statement string to the default state
335 scDoc
.ChangeState(SCE_CLW_DEFAULT
);
337 // Terminate the keyword state and set to default state
338 scDoc
.SetState(SCE_CLW_DEFAULT
);
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
);
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
);
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
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
);
374 // Else parenthese level is greater than zero
375 // still looking for matching parentheses
377 // Decrement the parenthese level
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
);
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
)
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
);
403 // Else the number was an integer
405 // Colour the matched string to an integer constant state
406 scDoc
.ChangeState(SCE_CLW_INTEGER_CONSTANT
);
408 // Terminate the integer constant state and set to default state
409 scDoc
.SetState(SCE_CLW_DEFAULT
);
414 // Determine if a new state should be entered.
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
);
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
);
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
);
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
);
445 scDoc
.SetState(SCE_CLW_DEFAULT
);
447 // else an invalid character in column 1
449 // Set to error state
450 scDoc
.SetState(SCE_CLW_ERROR
);
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
);
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
);
468 else if (isdigit(scDoc
.ch
)) {
469 // Set the state to Integer Constant and verify later
470 scDoc
.SetState(SCE_CLW_INTEGER_CONSTANT
);
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
);
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
);
486 // move to the next character and then set the state to comment.
487 scDoc
.ForwardSetState(SCE_CLW_STRING
);
490 // else the character is an @ (ampersand)
491 else if (scDoc
.ch
== '@') {
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
);
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
);
516 // Clarion Language Case Sensitive Colouring Procedure
517 static void ColouriseClarionDocSensitive(unsigned int uiStartPos
, int iLength
, int iInitStyle
, WordList
*wlKeywords
[], Accessor
&accStyler
) {
519 ColouriseClarionDoc(uiStartPos
, iLength
, iInitStyle
, wlKeywords
, accStyler
, true);
522 // Clarion Language Case Insensitive Colouring Procedure
523 static void ColouriseClarionDocInsensitive(unsigned int uiStartPos
, int iLength
, int iInitStyle
, WordList
*wlKeywords
[], Accessor
&accStyler
) {
525 ColouriseClarionDoc(uiStartPos
, iLength
, iInitStyle
, wlKeywords
, accStyler
, false);
530 static void FillBuffer(unsigned int uiStart
, unsigned int uiEnd
, Accessor
&accStyler
, char *szBuffer
, unsigned int uiLength
) {
532 unsigned int uiPos
= 0;
534 while ((uiPos
< uiEnd
- uiStart
+ 1) && (uiPos
< uiLength
-1)) {
535 szBuffer
[uiPos
] = static_cast<char>(toupper(accStyler
[uiStart
+ uiPos
]));
538 szBuffer
[uiPos
] = '\0';
541 // Classify Clarion Fold Point
543 static int ClassifyClarionFoldPoint(int iLevel
, const char* szString
) {
545 if (!(isdigit(szString
[0]) || (szString
[0] == '.'))) {
546 if (strcmp(szString
, "PROCEDURE") == 0) {
547 // iLevel = SC_FOLDLEVELBASE + 1;
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) {
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) {
585 else if (strcmp(szString
, "END") == 0 ||
586 strcmp(szString
, "UNTIL") == 0 ||
587 strcmp(szString
, "WHILE") == 0) {
594 // Clarion Language Folding Procedure
595 static void FoldClarionDoc(unsigned int uiStartPos
, int iLength
, int iInitStyle
, WordList
*[], Accessor
&accStyler
) {
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;
607 for (unsigned int uiPos
= uiStartPos
; uiPos
< uiEndPos
; uiPos
++) {
609 char chChar
= chNext
;
610 chNext
= accStyler
.SafeGetCharAt(uiPos
+ 1);
611 int iStylePrev
= iStyle
;
613 iStyleNext
= accStyler
.StyleAt(uiPos
+ 1);
614 bool bEOL
= (chChar
== '\r' && chNext
!= '\n') || (chChar
== '\n');
616 if (iStylePrev
== SCE_CLW_DEFAULT
) {
617 if (iStyle
== SCE_CLW_KEYWORD
|| iStyle
== SCE_CLW_STRUCTURE_DATA_TYPE
) {
618 // Store last word start point.
623 if (iStylePrev
== SCE_CLW_KEYWORD
|| iStylePrev
== SCE_CLW_STRUCTURE_DATA_TYPE
) {
624 if(iswordchar(chChar
) && !iswordchar(chNext
)) {
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;
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
);
643 iLevelPrev
= iLevelCurrent
;
647 if (!isspacechar(chChar
))
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
);
657 // Word List Descriptions
658 static const char * const rgWordListDescriptions
[] = {
660 "Compiler Directives",
661 "Built-in Procedures and Functions",
662 "Runtime Expressions",
663 "Structure and Data Types",
666 "Reserved Words (Labels)",
667 "Reserved Words (Procedure Labels)",
671 // Case Sensitive Clarion Language Lexer
672 LexerModule
lmClw(SCLEX_CLW
, ColouriseClarionDocSensitive
, "clarion", FoldClarionDoc
, rgWordListDescriptions
);
674 // Case Insensitive Clarion Language Lexer
675 LexerModule
lmClwNoCase(SCLEX_CLWNOCASE
, ColouriseClarionDocInsensitive
, "clarionnocase", FoldClarionDoc
, rgWordListDescriptions
);