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.
17 #include "Scintilla.h"
21 #include "LexAccessor.h"
23 #include "StyleContext.h"
24 #include "CharacterSet.h"
25 #include "LexerModule.h"
28 using namespace Scintilla
;
31 // Is an end of line character
32 inline bool IsEOL(const int ch
) {
37 // Convert character to uppercase
38 static char CharacterUpper(char chChar
) {
40 if (chChar
< 'a' || chChar
> 'z') {
44 return(static_cast<char>(chChar
- 'a' + 'A'));
48 // Convert string to uppercase
49 static void StringUpper(char *szString
) {
52 *szString
= CharacterUpper(*szString
);
57 // Is a label start character
58 inline bool IsALabelStart(const int iChar
) {
60 return(isalpha(iChar
) || iChar
== '_');
63 // Is a label character
64 inline bool IsALabelCharacter(const int iChar
) {
66 return(isalnum(iChar
) || iChar
== '_' || iChar
== ':');
69 // Is the character is a ! and the the next character is not a !
70 inline bool IsACommentStart(const int iChar
) {
75 // Is the character a Clarion hex character (ABCDEF)
76 inline bool IsAHexCharacter(const int iChar
, bool bCaseSensitive
) {
79 if (!bCaseSensitive
) {
80 if (strchr("ABCDEFabcdef", iChar
) != NULL
) {
86 if (strchr("ABCDEF", iChar
) != NULL
) {
93 // Is the character a Clarion base character (B=Binary, O=Octal, H=Hex)
94 inline bool IsANumericBaseCharacter(const int iChar
, bool bCaseSensitive
) {
97 if (!bCaseSensitive
) {
98 // If character is a numeric base character
99 if (strchr("BOHboh", iChar
) != NULL
) {
105 // If character is a numeric base character
106 if (strchr("BOH", iChar
) != NULL
) {
113 // Set the correct numeric constant state
114 inline bool SetNumericConstantState(StyleContext
&scDoc
) {
116 int iPoints
= 0; // Point counter
117 char cNumericString
[512]; // Numeric string buffer
119 // Buffer the current numberic string
120 scDoc
.GetCurrent(cNumericString
, sizeof(cNumericString
));
121 // Loop through the string until end of string (NULL termination)
122 for (int iIndex
= 0; cNumericString
[iIndex
] != '\0'; iIndex
++) {
123 // Depending on the character
124 switch (cNumericString
[iIndex
]) {
127 // Increment point counter
134 // If points found (can be more than one for improper formatted number
138 // Else no points found
144 // Get the next word in uppercase from the current position (keyword lookahead)
145 inline bool GetNextWordUpper(Accessor
&styler
, unsigned int uiStartPos
, int iLength
, char *cWord
) {
147 unsigned int iIndex
= 0; // Buffer Index
149 // Loop through the remaining string from the current position
150 for (int iOffset
= uiStartPos
; iOffset
< iLength
; iOffset
++) {
151 // Get the character from the buffer using the offset
152 char cCharacter
= styler
[iOffset
];
153 if (IsEOL(cCharacter
)) {
156 // If the character is alphabet character
157 if (isalpha(cCharacter
)) {
158 // Add UPPERCASE character to the word buffer
159 cWord
[iIndex
++] = CharacterUpper(cCharacter
);
162 // Add null termination
163 cWord
[iIndex
] = '\0';
164 // If no word was found
169 // Else word was found
176 // Clarion Language Colouring Procedure
177 static void ColouriseClarionDoc(unsigned int uiStartPos
, int iLength
, int iInitStyle
, WordList
*wlKeywords
[], Accessor
&accStyler
, bool bCaseSensitive
) {
179 int iParenthesesLevel
= 0; // Parenthese Level
180 int iColumn1Label
= false; // Label starts in Column 1
182 WordList
&wlClarionKeywords
= *wlKeywords
[0]; // Clarion Keywords
183 WordList
&wlCompilerDirectives
= *wlKeywords
[1]; // Compiler Directives
184 WordList
&wlRuntimeExpressions
= *wlKeywords
[2]; // Runtime Expressions
185 WordList
&wlBuiltInProcsFuncs
= *wlKeywords
[3]; // Builtin Procedures and Functions
186 WordList
&wlStructsDataTypes
= *wlKeywords
[4]; // Structures and Data Types
187 WordList
&wlAttributes
= *wlKeywords
[5]; // Procedure Attributes
188 WordList
&wlStandardEquates
= *wlKeywords
[6]; // Standard Equates
189 WordList
&wlLabelReservedWords
= *wlKeywords
[7]; // Clarion Reserved Keywords (Labels)
190 WordList
&wlProcLabelReservedWords
= *wlKeywords
[8]; // Clarion Reserved Keywords (Procedure Labels)
192 const char wlProcReservedKeywordList
[] =
193 "PROCEDURE FUNCTION";
194 WordList wlProcReservedKeywords
;
195 wlProcReservedKeywords
.Set(wlProcReservedKeywordList
);
197 const char wlCompilerKeywordList
[] =
199 WordList wlCompilerKeywords
;
200 wlCompilerKeywords
.Set(wlCompilerKeywordList
);
202 const char wlLegacyStatementsList
[] =
203 "BOF EOF FUNCTION POINTER SHARE";
204 WordList wlLegacyStatements
;
205 wlLegacyStatements
.Set(wlLegacyStatementsList
);
207 StyleContext
scDoc(uiStartPos
, iLength
, iInitStyle
, accStyler
);
210 for (; scDoc
.More(); scDoc
.Forward())
213 // Determine if the current state should terminate.
216 // Label State Handling
217 if (scDoc
.state
== SCE_CLW_LABEL
) {
218 // If the character is not a valid label
219 if (!IsALabelCharacter(scDoc
.ch
)) {
220 // If the character is a . (dot syntax)
221 if (scDoc
.ch
== '.') {
222 // Turn off column 1 label flag as label now cannot be reserved work
223 iColumn1Label
= false;
224 // Uncolour the . (dot) to default state, move forward one character,
225 // and change back to the label state.
226 scDoc
.SetState(SCE_CLW_DEFAULT
);
228 scDoc
.SetState(SCE_CLW_LABEL
);
232 char cLabel
[512]; // Label buffer
233 // Buffer the current label string
234 scDoc
.GetCurrent(cLabel
,sizeof(cLabel
));
235 // If case insensitive, convert string to UPPERCASE to match passed keywords.
236 if (!bCaseSensitive
) {
239 // Else if UPPERCASE label string is in the Clarion compiler keyword list
240 if (wlCompilerKeywords
.InList(cLabel
) && iColumn1Label
){
241 // change the label to error state
242 scDoc
.ChangeState(SCE_CLW_COMPILER_DIRECTIVE
);
244 // Else if UPPERCASE label string is in the Clarion reserved keyword list
245 else if (wlLabelReservedWords
.InList(cLabel
) && iColumn1Label
){
246 // change the label to error state
247 scDoc
.ChangeState(SCE_CLW_ERROR
);
249 // Else if UPPERCASE label string is
250 else if (wlProcLabelReservedWords
.InList(cLabel
) && iColumn1Label
) {
251 char cWord
[512]; // Word buffer
252 // Get the next word from the current position
253 if (GetNextWordUpper(accStyler
,scDoc
.currentPos
,uiStartPos
+iLength
,cWord
)) {
254 // If the next word is a procedure reserved word
255 if (wlProcReservedKeywords
.InList(cWord
)) {
256 // Change the label to error state
257 scDoc
.ChangeState(SCE_CLW_ERROR
);
261 // Else if label string is in the compiler directive keyword list
262 else if (wlCompilerDirectives
.InList(cLabel
)) {
263 // change the state to compiler directive state
264 scDoc
.ChangeState(SCE_CLW_COMPILER_DIRECTIVE
);
266 // Terminate the label state and set to default state
267 scDoc
.SetState(SCE_CLW_DEFAULT
);
271 // Keyword State Handling
272 else if (scDoc
.state
== SCE_CLW_KEYWORD
) {
273 // If character is : (colon)
274 if (scDoc
.ch
== ':') {
275 char cEquate
[512]; // Equate buffer
276 // Move forward to include : (colon) in buffer
278 // Buffer the equate string
279 scDoc
.GetCurrent(cEquate
,sizeof(cEquate
));
280 // If case insensitive, convert string to UPPERCASE to match passed keywords.
281 if (!bCaseSensitive
) {
282 StringUpper(cEquate
);
284 // If statement string is in the equate list
285 if (wlStandardEquates
.InList(cEquate
)) {
286 // Change to equate state
287 scDoc
.ChangeState(SCE_CLW_STANDARD_EQUATE
);
290 // If the character is not a valid label character
291 else if (!IsALabelCharacter(scDoc
.ch
)) {
292 char cStatement
[512]; // Statement buffer
293 // Buffer the statement string
294 scDoc
.GetCurrent(cStatement
,sizeof(cStatement
));
295 // If case insensitive, convert string to UPPERCASE to match passed keywords.
296 if (!bCaseSensitive
) {
297 StringUpper(cStatement
);
299 // If statement string is in the Clarion keyword list
300 if (wlClarionKeywords
.InList(cStatement
)) {
301 // Change the statement string to the Clarion keyword state
302 scDoc
.ChangeState(SCE_CLW_KEYWORD
);
304 // Else if statement string is in the compiler directive keyword list
305 else if (wlCompilerDirectives
.InList(cStatement
)) {
306 // Change the statement string to the compiler directive state
307 scDoc
.ChangeState(SCE_CLW_COMPILER_DIRECTIVE
);
309 // Else if statement string is in the runtime expressions keyword list
310 else if (wlRuntimeExpressions
.InList(cStatement
)) {
311 // Change the statement string to the runtime expressions state
312 scDoc
.ChangeState(SCE_CLW_RUNTIME_EXPRESSIONS
);
314 // Else if statement string is in the builtin procedures and functions keyword list
315 else if (wlBuiltInProcsFuncs
.InList(cStatement
)) {
316 // Change the statement string to the builtin procedures and functions state
317 scDoc
.ChangeState(SCE_CLW_BUILTIN_PROCEDURES_FUNCTION
);
319 // Else if statement string is in the tructures and data types keyword list
320 else if (wlStructsDataTypes
.InList(cStatement
)) {
321 // Change the statement string to the structures and data types state
322 scDoc
.ChangeState(SCE_CLW_STRUCTURE_DATA_TYPE
);
324 // Else if statement string is in the procedure attribute keyword list
325 else if (wlAttributes
.InList(cStatement
)) {
326 // Change the statement string to the procedure attribute state
327 scDoc
.ChangeState(SCE_CLW_ATTRIBUTE
);
329 // Else if statement string is in the standard equate keyword list
330 else if (wlStandardEquates
.InList(cStatement
)) {
331 // Change the statement string to the standard equate state
332 scDoc
.ChangeState(SCE_CLW_STANDARD_EQUATE
);
334 // Else if statement string is in the deprecated or legacy keyword list
335 else if (wlLegacyStatements
.InList(cStatement
)) {
336 // Change the statement string to the standard equate state
337 scDoc
.ChangeState(SCE_CLW_DEPRECATED
);
339 // Else the statement string doesn't match any work list
341 // Change the statement string to the default state
342 scDoc
.ChangeState(SCE_CLW_DEFAULT
);
344 // Terminate the keyword state and set to default state
345 scDoc
.SetState(SCE_CLW_DEFAULT
);
348 // String State Handling
349 else if (scDoc
.state
== SCE_CLW_STRING
) {
350 // If the character is an ' (single quote)
351 if (scDoc
.ch
== '\'') {
352 // Set the state to default and move forward colouring
353 // the ' (single quote) as default state
354 // terminating the string state
355 scDoc
.SetState(SCE_CLW_DEFAULT
);
358 // If the next character is an ' (single quote)
359 if (scDoc
.chNext
== '\'') {
360 // Move forward one character and set to default state
361 // colouring the next ' (single quote) as default state
362 // terminating the string state
363 scDoc
.ForwardSetState(SCE_CLW_DEFAULT
);
367 // Picture String State Handling
368 else if (scDoc
.state
== SCE_CLW_PICTURE_STRING
) {
369 // If the character is an ( (open parenthese)
370 if (scDoc
.ch
== '(') {
371 // Increment the parenthese level
374 // Else if the character is a ) (close parenthese)
375 else if (scDoc
.ch
== ')') {
376 // If the parenthese level is set to zero
377 // parentheses matched
378 if (!iParenthesesLevel
) {
379 scDoc
.SetState(SCE_CLW_DEFAULT
);
381 // Else parenthese level is greater than zero
382 // still looking for matching parentheses
384 // Decrement the parenthese level
389 // Standard Equate State Handling
390 else if (scDoc
.state
== SCE_CLW_STANDARD_EQUATE
) {
391 if (!isalnum(scDoc
.ch
)) {
392 scDoc
.SetState(SCE_CLW_DEFAULT
);
395 // Integer Constant State Handling
396 else if (scDoc
.state
== SCE_CLW_INTEGER_CONSTANT
) {
397 // If the character is not a digit (0-9)
398 // or character is not a hexidecimal character (A-F)
399 // or character is not a . (point)
400 // or character is not a numberic base character (B,O,H)
401 if (!(isdigit(scDoc
.ch
)
402 || IsAHexCharacter(scDoc
.ch
, bCaseSensitive
)
404 || IsANumericBaseCharacter(scDoc
.ch
, bCaseSensitive
))) {
405 // If the number was a real
406 if (SetNumericConstantState(scDoc
)) {
407 // Colour the matched string to the real constant state
408 scDoc
.ChangeState(SCE_CLW_REAL_CONSTANT
);
410 // Else the number was an integer
412 // Colour the matched string to an integer constant state
413 scDoc
.ChangeState(SCE_CLW_INTEGER_CONSTANT
);
415 // Terminate the integer constant state and set to default state
416 scDoc
.SetState(SCE_CLW_DEFAULT
);
421 // Determine if a new state should be entered.
424 // Beginning of Line Handling
425 if (scDoc
.atLineStart
) {
426 // Reset the column 1 label flag
427 iColumn1Label
= false;
428 // If column 1 character is a label start character
429 if (IsALabelStart(scDoc
.ch
)) {
430 // Label character is found in column 1
431 // so set column 1 label flag and clear last column 1 label
432 iColumn1Label
= true;
433 // Set the state to label
434 scDoc
.SetState(SCE_CLW_LABEL
);
436 // else if character is a space or tab
437 else if (IsASpace(scDoc
.ch
)){
438 // Set to default state
439 scDoc
.SetState(SCE_CLW_DEFAULT
);
441 // else if comment start (!) or is an * (asterisk)
442 else if (IsACommentStart(scDoc
.ch
) || scDoc
.ch
== '*' ) {
443 // then set the state to comment.
444 scDoc
.SetState(SCE_CLW_COMMENT
);
446 // else the character is a ? (question mark)
447 else if (scDoc
.ch
== '?') {
448 // Change to the compiler directive state, move forward,
449 // colouring the ? (question mark), change back to default state.
450 scDoc
.ChangeState(SCE_CLW_COMPILER_DIRECTIVE
);
452 scDoc
.SetState(SCE_CLW_DEFAULT
);
454 // else an invalid character in column 1
456 // Set to error state
457 scDoc
.SetState(SCE_CLW_ERROR
);
460 // End of Line Handling
461 else if (scDoc
.atLineEnd
) {
462 // Reset to the default state at the end of each line.
463 scDoc
.SetState(SCE_CLW_DEFAULT
);
467 // If in default state
468 if (scDoc
.state
== SCE_CLW_DEFAULT
) {
469 // If is a letter could be a possible statement
470 if (isalpha(scDoc
.ch
)) {
471 // Set the state to Clarion Keyword and verify later
472 scDoc
.SetState(SCE_CLW_KEYWORD
);
475 else if (isdigit(scDoc
.ch
)) {
476 // Set the state to Integer Constant and verify later
477 scDoc
.SetState(SCE_CLW_INTEGER_CONSTANT
);
479 // else if the start of a comment or a | (line continuation)
480 else if (IsACommentStart(scDoc
.ch
) || scDoc
.ch
== '|') {
481 // then set the state to comment.
482 scDoc
.SetState(SCE_CLW_COMMENT
);
484 // else if the character is a ' (single quote)
485 else if (scDoc
.ch
== '\'') {
486 // If the character is also a ' (single quote)
487 // Embedded Apostrophe
488 if (scDoc
.chNext
== '\'') {
489 // Move forward colouring it as default state
490 scDoc
.ForwardSetState(SCE_CLW_DEFAULT
);
493 // move to the next character and then set the state to comment.
494 scDoc
.ForwardSetState(SCE_CLW_STRING
);
497 // else the character is an @ (ampersand)
498 else if (scDoc
.ch
== '@') {
500 if (!bCaseSensitive
) {
501 // If character is a valid picture token character
502 if (strchr("DEKNPSTdeknpst", scDoc
.chNext
) != NULL
) {
503 // Set to the picture string state
504 scDoc
.SetState(SCE_CLW_PICTURE_STRING
);
509 // If character is a valid picture token character
510 if (strchr("DEKNPST", scDoc
.chNext
) != NULL
) {
511 // Set the picture string state
512 scDoc
.SetState(SCE_CLW_PICTURE_STRING
);
523 // Clarion Language Case Sensitive Colouring Procedure
524 static void ColouriseClarionDocSensitive(unsigned int uiStartPos
, int iLength
, int iInitStyle
, WordList
*wlKeywords
[], Accessor
&accStyler
) {
526 ColouriseClarionDoc(uiStartPos
, iLength
, iInitStyle
, wlKeywords
, accStyler
, true);
529 // Clarion Language Case Insensitive Colouring Procedure
530 static void ColouriseClarionDocInsensitive(unsigned int uiStartPos
, int iLength
, int iInitStyle
, WordList
*wlKeywords
[], Accessor
&accStyler
) {
532 ColouriseClarionDoc(uiStartPos
, iLength
, iInitStyle
, wlKeywords
, accStyler
, false);
537 static void FillBuffer(unsigned int uiStart
, unsigned int uiEnd
, Accessor
&accStyler
, char *szBuffer
, unsigned int uiLength
) {
539 unsigned int uiPos
= 0;
541 while ((uiPos
< uiEnd
- uiStart
+ 1) && (uiPos
< uiLength
-1)) {
542 szBuffer
[uiPos
] = static_cast<char>(toupper(accStyler
[uiStart
+ uiPos
]));
545 szBuffer
[uiPos
] = '\0';
548 // Classify Clarion Fold Point
550 static int ClassifyClarionFoldPoint(int iLevel
, const char* szString
) {
552 if (!(isdigit(szString
[0]) || (szString
[0] == '.'))) {
553 if (strcmp(szString
, "PROCEDURE") == 0) {
554 // iLevel = SC_FOLDLEVELBASE + 1;
556 else if (strcmp(szString
, "MAP") == 0 ||
557 strcmp(szString
,"ACCEPT") == 0 ||
558 strcmp(szString
,"BEGIN") == 0 ||
559 strcmp(szString
,"CASE") == 0 ||
560 strcmp(szString
,"EXECUTE") == 0 ||
561 strcmp(szString
,"IF") == 0 ||
562 strcmp(szString
,"ITEMIZE") == 0 ||
563 strcmp(szString
,"INTERFACE") == 0 ||
564 strcmp(szString
,"JOIN") == 0 ||
565 strcmp(szString
,"LOOP") == 0 ||
566 strcmp(szString
,"MODULE") == 0 ||
567 strcmp(szString
,"RECORD") == 0) {
570 else if (strcmp(szString
, "APPLICATION") == 0 ||
571 strcmp(szString
, "CLASS") == 0 ||
572 strcmp(szString
, "DETAIL") == 0 ||
573 strcmp(szString
, "FILE") == 0 ||
574 strcmp(szString
, "FOOTER") == 0 ||
575 strcmp(szString
, "FORM") == 0 ||
576 strcmp(szString
, "GROUP") == 0 ||
577 strcmp(szString
, "HEADER") == 0 ||
578 strcmp(szString
, "INTERFACE") == 0 ||
579 strcmp(szString
, "MENU") == 0 ||
580 strcmp(szString
, "MENUBAR") == 0 ||
581 strcmp(szString
, "OLE") == 0 ||
582 strcmp(szString
, "OPTION") == 0 ||
583 strcmp(szString
, "QUEUE") == 0 ||
584 strcmp(szString
, "REPORT") == 0 ||
585 strcmp(szString
, "SHEET") == 0 ||
586 strcmp(szString
, "TAB") == 0 ||
587 strcmp(szString
, "TOOLBAR") == 0 ||
588 strcmp(szString
, "VIEW") == 0 ||
589 strcmp(szString
, "WINDOW") == 0) {
592 else if (strcmp(szString
, "END") == 0 ||
593 strcmp(szString
, "UNTIL") == 0 ||
594 strcmp(szString
, "WHILE") == 0) {
601 // Clarion Language Folding Procedure
602 static void FoldClarionDoc(unsigned int uiStartPos
, int iLength
, int iInitStyle
, WordList
*[], Accessor
&accStyler
) {
604 unsigned int uiEndPos
= uiStartPos
+ iLength
;
605 int iLineCurrent
= accStyler
.GetLine(uiStartPos
);
606 int iLevelPrev
= accStyler
.LevelAt(iLineCurrent
) & SC_FOLDLEVELNUMBERMASK
;
607 int iLevelCurrent
= iLevelPrev
;
608 char chNext
= accStyler
[uiStartPos
];
609 int iStyle
= iInitStyle
;
610 int iStyleNext
= accStyler
.StyleAt(uiStartPos
);
611 int iVisibleChars
= 0;
614 for (unsigned int uiPos
= uiStartPos
; uiPos
< uiEndPos
; uiPos
++) {
616 char chChar
= chNext
;
617 chNext
= accStyler
.SafeGetCharAt(uiPos
+ 1);
618 int iStylePrev
= iStyle
;
620 iStyleNext
= accStyler
.StyleAt(uiPos
+ 1);
621 bool bEOL
= (chChar
== '\r' && chNext
!= '\n') || (chChar
== '\n');
623 if (iStylePrev
== SCE_CLW_DEFAULT
) {
624 if (iStyle
== SCE_CLW_KEYWORD
|| iStyle
== SCE_CLW_STRUCTURE_DATA_TYPE
) {
625 // Store last word start point.
630 if (iStylePrev
== SCE_CLW_KEYWORD
|| iStylePrev
== SCE_CLW_STRUCTURE_DATA_TYPE
) {
631 if(iswordchar(chChar
) && !iswordchar(chNext
)) {
633 FillBuffer(iLastStart
, uiPos
, accStyler
, chBuffer
, sizeof(chBuffer
));
634 iLevelCurrent
= ClassifyClarionFoldPoint(iLevelCurrent
,chBuffer
);
635 // if ((iLevelCurrent == SC_FOLDLEVELBASE + 1) && iLineCurrent > 1) {
636 // accStyler.SetLevel(iLineCurrent-1,SC_FOLDLEVELBASE);
637 // iLevelPrev = SC_FOLDLEVELBASE;
643 int iLevel
= iLevelPrev
;
644 if ((iLevelCurrent
> iLevelPrev
) && (iVisibleChars
> 0))
645 iLevel
|= SC_FOLDLEVELHEADERFLAG
;
646 if (iLevel
!= accStyler
.LevelAt(iLineCurrent
)) {
647 accStyler
.SetLevel(iLineCurrent
,iLevel
);
650 iLevelPrev
= iLevelCurrent
;
654 if (!isspacechar(chChar
))
658 // Fill in the real level of the next line, keeping the current flags
659 // as they will be filled in later.
660 int iFlagsNext
= accStyler
.LevelAt(iLineCurrent
) & ~SC_FOLDLEVELNUMBERMASK
;
661 accStyler
.SetLevel(iLineCurrent
, iLevelPrev
| iFlagsNext
);
664 // Word List Descriptions
665 static const char * const rgWordListDescriptions
[] = {
667 "Compiler Directives",
668 "Built-in Procedures and Functions",
669 "Runtime Expressions",
670 "Structure and Data Types",
673 "Reserved Words (Labels)",
674 "Reserved Words (Procedure Labels)",
678 // Case Sensitive Clarion Language Lexer
679 LexerModule
lmClw(SCLEX_CLW
, ColouriseClarionDocSensitive
, "clarion", FoldClarionDoc
, rgWordListDescriptions
);
681 // Case Insensitive Clarion Language Lexer
682 LexerModule
lmClwNoCase(SCLEX_CLWNOCASE
, ColouriseClarionDocInsensitive
, "clarionnocase", FoldClarionDoc
, rgWordListDescriptions
);