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.
17 #include "Scintilla.h"
21 #include "LexAccessor.h"
23 #include "StyleContext.h"
24 #include "CharacterSet.h"
25 #include "LexerModule.h"
28 using namespace Scintilla
;
32 * Is it a core character (C isalpha(), exclamation and question mark)
34 * \param ch The character
35 * \return True if ch is a character, False otherwise
37 static inline bool IsAlphaCore(int ch
) {
38 return (isalpha(ch
) || ch
== '!' || ch
== '?');
42 * Is it a character (IsAlphaCore() and underscore)
44 * \param ch The character
45 * \return True if ch is a character, False otherwise
47 static inline bool IsAlpha(int ch
) {
48 return (IsAlphaCore(ch
) || ch
== '_');
52 * Is it a symbolic character (IsAlpha() and colon)
54 * \param ch The character
55 * \return True if ch is a character, False otherwise
57 static inline bool IsAlphaSym(int ch
) {
58 return (IsAlpha(ch
) || ch
== ':');
62 * Is it a numerical character (IsAlpha() and 0 - 9)
64 * \param ch The character
65 * \return True if ch is a character, False otherwise
67 static inline bool IsAlNum(int ch
) {
68 return ((ch
>= '0' && ch
<= '9') || IsAlpha(ch
));
72 * Is it a symbolic numerical character (IsAlNum() and colon)
74 * \param ch The character
75 * \return True if ch is a character, False otherwise
77 static inline bool IsAlNumSym(int ch
) {
78 return (IsAlNum(ch
) || ch
== ':');
84 * \param startPos Where to start scanning
85 * \param length Where to scan to
86 * \param initStyle The style at the initial point, not used in this folder
87 * \param keywordslists The keywordslists, currently, number 5 is used
88 * \param styler The styler
90 static void ColouriseMagikDoc(unsigned int startPos
, int length
, int initStyle
,
91 WordList
*keywordlists
[], Accessor
&styler
) {
92 styler
.StartAt(startPos
);
94 WordList
&keywords
= *keywordlists
[0];
95 WordList
&pragmatics
= *keywordlists
[1];
96 WordList
&containers
= *keywordlists
[2];
97 WordList
&flow
= *keywordlists
[3];
98 WordList
&characters
= *keywordlists
[4];
100 StyleContext
sc(startPos
, length
, initStyle
, styler
);
103 for (; sc
.More(); sc
.Forward()) {
108 if (sc
.chNext
== '#') sc
.SetState(SCE_MAGIK_HYPER_COMMENT
);
109 else sc
.SetState(SCE_MAGIK_COMMENT
);
110 for(; sc
.More() && !(sc
.atLineEnd
); sc
.Forward());
111 sc
.SetState(SCE_MAGIK_DEFAULT
);
116 sc
.SetState(SCE_MAGIK_STRING
);
121 for(; sc
.More() && sc
.ch
!= '"'; sc
.Forward());
124 sc
.ForwardSetState(SCE_MAGIK_DEFAULT
);
129 if(sc
.state
== SCE_MAGIK_DEFAULT
) {
131 // A certain keyword has been detected
132 if (sc
.ch
== '_' && (
133 sc
.currentPos
== 0 || !IsAlNum(sc
.chPrev
))) {
135 memset(keyword
, '\0', 50);
138 int scanPosition
= 0;
141 char keywordChar
= static_cast<char>(
142 tolower(styler
.SafeGetCharAt(
144 static_cast<int>(sc
.currentPos
+1), ' ')));
145 if(IsAlpha(keywordChar
)) {
146 keyword
[scanPosition
] = keywordChar
;
153 if(pragmatics
.InList(keyword
)) {
154 sc
.SetState(SCE_MAGIK_PRAGMA
);
157 // it is a normal keyword like _local, _self, etc.
158 else if(keywords
.InList(keyword
)) {
159 sc
.SetState(SCE_MAGIK_KEYWORD
);
162 // It is a container keyword, such as _method, _proc, etc.
163 else if(containers
.InList(keyword
)) {
164 sc
.SetState(SCE_MAGIK_CONTAINER
);
167 // It is a flow keyword, such as _for, _if, _try, etc.
168 else if(flow
.InList(keyword
)) {
169 sc
.SetState(SCE_MAGIK_FLOW
);
172 // Interpret as unknown keyword
174 sc
.SetState(SCE_MAGIK_UNKNOWN_KEYWORD
);
178 // Symbolic expression
179 else if(sc
.ch
== ':' && !IsAlNum(sc
.chPrev
)) {
180 sc
.SetState(SCE_MAGIK_SYMBOL
);
181 bool firstTrip
= true;
182 for(sc
.Forward(); sc
.More(); sc
.Forward()) {
183 if(firstTrip
&& IsAlphaSym(sc
.ch
));
184 else if(!firstTrip
&& IsAlNumSym(sc
.ch
));
185 else if(sc
.ch
== '|') {
187 sc
.More() && sc
.ch
!= '|';
194 sc
.SetState(SCE_MAGIK_DEFAULT
);
198 // Identifier (label) expression
199 else if(sc
.ch
== '@') {
200 sc
.SetState(SCE_MAGIK_IDENTIFIER
);
201 bool firstTrip
= true;
202 for(sc
.Forward(); sc
.More(); sc
.Forward()) {
203 if(firstTrip
&& IsAlphaCore(sc
.ch
)) {
206 else if(!firstTrip
&& IsAlpha(sc
.ch
));
209 sc
.SetState(SCE_MAGIK_DEFAULT
);
213 // Start of a character
214 else if(sc
.ch
== '%') {
215 sc
.SetState(SCE_MAGIK_CHARACTER
);
218 memset(keyword
, '\0', 50);
221 int scanPosition
= 0;
224 char keywordChar
= static_cast<char>(
225 tolower(styler
.SafeGetCharAt(
227 static_cast<int>(sc
.currentPos
), ' ')));
228 if(IsAlpha(keywordChar
)) {
229 keyword
[scanPosition
] = keywordChar
;
235 if(characters
.InList(keyword
)) {
236 sc
.Forward(static_cast<int>(strlen(keyword
)));
241 sc
.SetState(SCE_MAGIK_DEFAULT
);
258 sc
.SetState(SCE_MAGIK_OPERATOR
);
262 else if(sc
.ch
== '(' || sc
.ch
== ')') {
263 sc
.SetState(SCE_MAGIK_BRACE_BLOCK
);
267 else if(sc
.ch
== '{' || sc
.ch
== '}') {
268 sc
.SetState(SCE_MAGIK_BRACKET_BLOCK
);
272 else if(sc
.ch
== '[' || sc
.ch
== ']') {
273 sc
.SetState(SCE_MAGIK_SQBRACKET_BLOCK
);
281 sc
.state
== SCE_MAGIK_OPERATOR
||
282 sc
.state
== SCE_MAGIK_BRACE_BLOCK
||
283 sc
.state
== SCE_MAGIK_BRACKET_BLOCK
||
284 sc
.state
== SCE_MAGIK_SQBRACKET_BLOCK
) {
285 sc
.SetState(SCE_MAGIK_DEFAULT
);
289 // It is the pragma state
290 else if(sc
.state
== SCE_MAGIK_PRAGMA
) {
291 if(!IsAlpha(sc
.ch
)) {
292 sc
.SetState(SCE_MAGIK_DEFAULT
);
297 // It is the keyword state
299 sc
.state
== SCE_MAGIK_KEYWORD
||
300 sc
.state
== SCE_MAGIK_CONTAINER
||
301 sc
.state
== SCE_MAGIK_FLOW
||
302 sc
.state
== SCE_MAGIK_UNKNOWN_KEYWORD
) {
303 if(!IsAlpha(sc
.ch
)) {
304 sc
.SetState(SCE_MAGIK_DEFAULT
);
314 * The word list description
316 static const char * const magikWordListDesc
[] = {
317 "Accessors (local, global, self, super, thisthread)",
318 "Pragmatic (pragma, private)",
319 "Containers (method, block, proc)",
320 "Flow (if, then, elif, else)",
321 "Characters (space, tab, newline, return)",
322 "Fold Containers (method, proc, block, if, loop)",
326 * This function detects keywords which are able to have a body. Note that it
327 * uses the Fold Containers word description, not the containers description. It
328 * only works when the style at that particular position is set on Containers
329 * or Flow (number 3 or 4).
331 * \param keywordslist The list of keywords that are scanned, they should only
332 * contain the start keywords, not the end keywords
333 * \param The actual keyword
334 * \return 1 if it is a folding start-keyword, -1 if it is a folding end-keyword
337 static inline int IsFoldingContainer(WordList
&keywordslist
, char * keyword
) {
339 strlen(keyword
) > 3 &&
340 keyword
[0] == 'e' && keyword
[1] == 'n' && keyword
[2] == 'd') {
341 if (keywordslist
.InList(keyword
+ 3)) {
346 if(keywordslist
.InList(keyword
)) {
355 * The folding function
357 * \param startPos Where to start scanning
358 * \param length Where to scan to
359 * \param keywordslists The keywordslists, currently, number 5 is used
360 * \param styler The styler
362 static void FoldMagikDoc(unsigned int startPos
, int length
, int,
363 WordList
*keywordslists
[], Accessor
&styler
) {
365 bool compact
= styler
.GetPropertyInt("fold.compact") != 0;
367 WordList
&foldingElements
= *keywordslists
[5];
368 int endPos
= startPos
+ length
;
369 int line
= styler
.GetLine(startPos
);
370 int level
= styler
.LevelAt(line
) & SC_FOLDLEVELNUMBERMASK
;
371 int flags
= styler
.LevelAt(line
) & ~SC_FOLDLEVELNUMBERMASK
;
374 int currentPos
= startPos
;
377 char currentState
= styler
.StyleAt(currentPos
);
378 char c
= styler
.SafeGetCharAt(currentPos
, ' ');
379 int prevLine
= styler
.GetLine(currentPos
- 1);
380 line
= styler
.GetLine(currentPos
);
383 if(prevLine
< line
) {
384 styler
.SetLevel(line
, (level
|flags
) & ~SC_FOLDLEVELHEADERFLAG
);
385 flags
= styler
.LevelAt(line
) & ~SC_FOLDLEVELNUMBERMASK
;
390 currentState
== SCE_MAGIK_CONTAINER
||
391 currentState
== SCE_MAGIK_FLOW
396 memset(keyword
, '\0', 50);
399 int scanPosition
= 0;
402 char keywordChar
= static_cast<char>(
403 tolower(styler
.SafeGetCharAt(
405 currentPos
+ 1, ' ')));
406 if(IsAlpha(keywordChar
)) {
407 keyword
[scanPosition
] = keywordChar
;
413 if(IsFoldingContainer(foldingElements
, keyword
) > 0) {
416 styler
.LevelAt(line
) | SC_FOLDLEVELHEADERFLAG
);
418 } else if(IsFoldingContainer(foldingElements
, keyword
) < 0) {
419 styler
.SetLevel(line
, styler
.LevelAt(line
));
426 currentState
== SCE_MAGIK_BRACE_BLOCK
||
427 currentState
== SCE_MAGIK_BRACKET_BLOCK
||
428 currentState
== SCE_MAGIK_SQBRACKET_BLOCK
)) {
429 if(c
== '{' || c
== '[' || c
== '(') {
432 styler
.LevelAt(line
) | SC_FOLDLEVELHEADERFLAG
);
434 } else if(c
== '}' || c
== ']' || c
== ')') {
435 styler
.SetLevel(line
, styler
.LevelAt(line
));
444 * Injecting the module
446 LexerModule
lmMagikSF(
447 SCLEX_MAGIK
, ColouriseMagikDoc
, "magiksf", FoldMagikDoc
, magikWordListDesc
);