1 // Scintilla source code edit control
4 * Lexer for GE(r) Smallworld(tm) MagikSF
6 // Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
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
;
29 * Is it a core character (C isalpha(), exclamation and question mark)
31 * \param ch The character
32 * \return True if ch is a character, False otherwise
34 static inline bool IsAlphaCore(int ch
) {
35 return (isalpha(ch
) || ch
== '!' || ch
== '?');
39 * Is it a character (IsAlphaCore() and underscore)
41 * \param ch The character
42 * \return True if ch is a character, False otherwise
44 static inline bool IsAlpha(int ch
) {
45 return (IsAlphaCore(ch
) || ch
== '_');
49 * Is it a symbolic character (IsAlpha() and colon)
51 * \param ch The character
52 * \return True if ch is a character, False otherwise
54 static inline bool IsAlphaSym(int ch
) {
55 return (IsAlpha(ch
) || ch
== ':');
59 * Is it a numerical character (IsAlpha() and 0 - 9)
61 * \param ch The character
62 * \return True if ch is a character, False otherwise
64 static inline bool IsAlNum(int ch
) {
65 return ((ch
>= '0' && ch
<= '9') || IsAlpha(ch
));
69 * Is it a symbolic numerical character (IsAlNum() and colon)
71 * \param ch The character
72 * \return True if ch is a character, False otherwise
74 static inline bool IsAlNumSym(int ch
) {
75 return (IsAlNum(ch
) || ch
== ':');
81 * \param startPos Where to start scanning
82 * \param length Where to scan to
83 * \param initStyle The style at the initial point, not used in this folder
84 * \param keywordslists The keywordslists, currently, number 5 is used
85 * \param styler The styler
87 static void ColouriseMagikDoc(unsigned int startPos
, int length
, int initStyle
,
88 WordList
*keywordlists
[], Accessor
&styler
) {
89 styler
.StartAt(startPos
);
91 WordList
&keywords
= *keywordlists
[0];
92 WordList
&pragmatics
= *keywordlists
[1];
93 WordList
&containers
= *keywordlists
[2];
94 WordList
&flow
= *keywordlists
[3];
95 WordList
&characters
= *keywordlists
[4];
97 StyleContext
sc(startPos
, length
, initStyle
, styler
);
100 for (; sc
.More(); sc
.Forward()) {
105 if (sc
.chNext
== '#') sc
.SetState(SCE_MAGIK_HYPER_COMMENT
);
106 else sc
.SetState(SCE_MAGIK_COMMENT
);
107 for(; sc
.More() && !(sc
.atLineEnd
); sc
.Forward());
108 sc
.SetState(SCE_MAGIK_DEFAULT
);
113 sc
.SetState(SCE_MAGIK_STRING
);
118 for(; sc
.More() && sc
.ch
!= '"'; sc
.Forward());
121 sc
.ForwardSetState(SCE_MAGIK_DEFAULT
);
126 if(sc
.state
== SCE_MAGIK_DEFAULT
) {
128 // A certain keyword has been detected
129 if (sc
.ch
== '_' && (
130 sc
.currentPos
== 0 || !IsAlNum(sc
.chPrev
))) {
132 memset(keyword
, '\0', 50);
135 int scanPosition
= 0;
138 char keywordChar
= static_cast<char>(
139 tolower(styler
.SafeGetCharAt(
141 static_cast<int>(sc
.currentPos
+1), ' ')));
142 if(IsAlpha(keywordChar
)) {
143 keyword
[scanPosition
] = keywordChar
;
150 if(pragmatics
.InList(keyword
)) {
151 sc
.SetState(SCE_MAGIK_PRAGMA
);
154 // it is a normal keyword like _local, _self, etc.
155 else if(keywords
.InList(keyword
)) {
156 sc
.SetState(SCE_MAGIK_KEYWORD
);
159 // It is a container keyword, such as _method, _proc, etc.
160 else if(containers
.InList(keyword
)) {
161 sc
.SetState(SCE_MAGIK_CONTAINER
);
164 // It is a flow keyword, such as _for, _if, _try, etc.
165 else if(flow
.InList(keyword
)) {
166 sc
.SetState(SCE_MAGIK_FLOW
);
169 // Interpret as unknown keyword
171 sc
.SetState(SCE_MAGIK_UNKNOWN_KEYWORD
);
175 // Symbolic expression
176 else if(sc
.ch
== ':' && !IsAlNum(sc
.chPrev
)) {
177 sc
.SetState(SCE_MAGIK_SYMBOL
);
178 bool firstTrip
= true;
179 for(sc
.Forward(); sc
.More(); sc
.Forward()) {
180 if(firstTrip
&& IsAlphaSym(sc
.ch
));
181 else if(!firstTrip
&& IsAlNumSym(sc
.ch
));
182 else if(sc
.ch
== '|') {
184 sc
.More() && sc
.ch
!= '|';
191 sc
.SetState(SCE_MAGIK_DEFAULT
);
195 // Identifier (label) expression
196 else if(sc
.ch
== '@') {
197 sc
.SetState(SCE_MAGIK_IDENTIFIER
);
198 bool firstTrip
= true;
199 for(sc
.Forward(); sc
.More(); sc
.Forward()) {
200 if(firstTrip
&& IsAlphaCore(sc
.ch
)) {
203 else if(!firstTrip
&& IsAlpha(sc
.ch
));
206 sc
.SetState(SCE_MAGIK_DEFAULT
);
210 // Start of a character
211 else if(sc
.ch
== '%') {
212 sc
.SetState(SCE_MAGIK_CHARACTER
);
215 memset(keyword
, '\0', 50);
218 int scanPosition
= 0;
221 char keywordChar
= static_cast<char>(
222 tolower(styler
.SafeGetCharAt(
224 static_cast<int>(sc
.currentPos
), ' ')));
225 if(IsAlpha(keywordChar
)) {
226 keyword
[scanPosition
] = keywordChar
;
232 if(characters
.InList(keyword
)) {
233 sc
.Forward(strlen(keyword
));
238 sc
.SetState(SCE_MAGIK_DEFAULT
);
255 sc
.SetState(SCE_MAGIK_OPERATOR
);
259 else if(sc
.ch
== '(' || sc
.ch
== ')') {
260 sc
.SetState(SCE_MAGIK_BRACE_BLOCK
);
264 else if(sc
.ch
== '{' || sc
.ch
== '}') {
265 sc
.SetState(SCE_MAGIK_BRACKET_BLOCK
);
269 else if(sc
.ch
== '[' || sc
.ch
== ']') {
270 sc
.SetState(SCE_MAGIK_SQBRACKET_BLOCK
);
278 sc
.state
== SCE_MAGIK_OPERATOR
||
279 sc
.state
== SCE_MAGIK_BRACE_BLOCK
||
280 sc
.state
== SCE_MAGIK_BRACKET_BLOCK
||
281 sc
.state
== SCE_MAGIK_SQBRACKET_BLOCK
) {
282 sc
.SetState(SCE_MAGIK_DEFAULT
);
286 // It is the pragma state
287 else if(sc
.state
== SCE_MAGIK_PRAGMA
) {
288 if(!IsAlpha(sc
.ch
)) {
289 sc
.SetState(SCE_MAGIK_DEFAULT
);
294 // It is the keyword state
296 sc
.state
== SCE_MAGIK_KEYWORD
||
297 sc
.state
== SCE_MAGIK_CONTAINER
||
298 sc
.state
== SCE_MAGIK_FLOW
||
299 sc
.state
== SCE_MAGIK_UNKNOWN_KEYWORD
) {
300 if(!IsAlpha(sc
.ch
)) {
301 sc
.SetState(SCE_MAGIK_DEFAULT
);
311 * The word list description
313 static const char * const magikWordListDesc
[] = {
314 "Accessors (local, global, self, super, thisthread)",
315 "Pragmatic (pragma, private)",
316 "Containers (method, block, proc)",
317 "Flow (if, then, elif, else)",
318 "Characters (space, tab, newline, return)",
319 "Fold Containers (method, proc, block, if, loop)",
323 * This function detects keywords which are able to have a body. Note that it
324 * uses the Fold Containers word description, not the containers description. It
325 * only works when the style at that particular position is set on Containers
326 * or Flow (number 3 or 4).
328 * \param keywordslist The list of keywords that are scanned, they should only
329 * contain the start keywords, not the end keywords
330 * \param The actual keyword
331 * \return 1 if it is a folding start-keyword, -1 if it is a folding end-keyword
334 static inline int IsFoldingContainer(WordList
&keywordslist
, char * keyword
) {
336 strlen(keyword
) > 3 &&
337 keyword
[0] == 'e' && keyword
[1] == 'n' && keyword
[2] == 'd') {
338 if (keywordslist
.InList(keyword
+ 3)) {
343 if(keywordslist
.InList(keyword
)) {
352 * The folding function
354 * \param startPos Where to start scanning
355 * \param length Where to scan to
356 * \param keywordslists The keywordslists, currently, number 5 is used
357 * \param styler The styler
359 static void FoldMagikDoc(unsigned int startPos
, int length
, int,
360 WordList
*keywordslists
[], Accessor
&styler
) {
362 bool compact
= styler
.GetPropertyInt("fold.compact") != 0;
364 WordList
&foldingElements
= *keywordslists
[5];
365 int endPos
= startPos
+ length
;
366 int line
= styler
.GetLine(startPos
);
367 int level
= styler
.LevelAt(line
) & SC_FOLDLEVELNUMBERMASK
;
368 int flags
= styler
.LevelAt(line
) & ~SC_FOLDLEVELNUMBERMASK
;
371 int currentPos
= startPos
;
374 char currentState
= styler
.StyleAt(currentPos
);
375 char c
= styler
.SafeGetCharAt(currentPos
, ' ');
376 int prevLine
= styler
.GetLine(currentPos
- 1);
377 line
= styler
.GetLine(currentPos
);
380 if(prevLine
< line
) {
381 styler
.SetLevel(line
, (level
|flags
) & ~SC_FOLDLEVELHEADERFLAG
);
382 flags
= styler
.LevelAt(line
) & ~SC_FOLDLEVELNUMBERMASK
;
387 currentState
== SCE_MAGIK_CONTAINER
||
388 currentState
== SCE_MAGIK_FLOW
393 memset(keyword
, '\0', 50);
396 int scanPosition
= 0;
399 char keywordChar
= static_cast<char>(
400 tolower(styler
.SafeGetCharAt(
402 currentPos
+ 1, ' ')));
403 if(IsAlpha(keywordChar
)) {
404 keyword
[scanPosition
] = keywordChar
;
410 if(IsFoldingContainer(foldingElements
, keyword
) > 0) {
413 styler
.LevelAt(line
) | SC_FOLDLEVELHEADERFLAG
);
415 } else if(IsFoldingContainer(foldingElements
, keyword
) < 0) {
416 styler
.SetLevel(line
, styler
.LevelAt(line
));
423 currentState
== SCE_MAGIK_BRACE_BLOCK
||
424 currentState
== SCE_MAGIK_BRACKET_BLOCK
||
425 currentState
== SCE_MAGIK_SQBRACKET_BLOCK
)) {
426 if(c
== '{' || c
== '[' || c
== '(') {
429 styler
.LevelAt(line
) | SC_FOLDLEVELHEADERFLAG
);
431 } else if(c
== '}' || c
== ']' || c
== ')') {
432 styler
.SetLevel(line
, styler
.LevelAt(line
));
441 * Injecting the module
443 LexerModule
lmMagikSF(
444 SCLEX_MAGIK
, ColouriseMagikDoc
, "magiksf", FoldMagikDoc
, magikWordListDesc
);