1 // Scintilla source code edit control
2 /**
3  * @file LexMagik.cxx
4  * Lexer for GE(r) Smallworld(tm) MagikSF
5  */
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.
8 
9 #include <stdlib.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <stdarg.h>
13 #include <assert.h>
14 #include <ctype.h>
15 
16 #include "ILexer.h"
17 #include "Scintilla.h"
18 #include "SciLexer.h"
19 
20 #include "WordList.h"
21 #include "LexAccessor.h"
22 #include "Accessor.h"
23 #include "StyleContext.h"
24 #include "CharacterSet.h"
25 #include "LexerModule.h"
26 
27 #ifdef SCI_NAMESPACE
28 using namespace Scintilla;
29 #endif
30 
31 /**
32  * Is it a core character (C isalpha(), exclamation and question mark)
33  *
34  * \param  ch The character
35  * \return True if ch is a character, False otherwise
36  */
IsAlphaCore(int ch)37 static inline bool IsAlphaCore(int ch) {
38     return (isalpha(ch) || ch == '!' || ch == '?');
39 }
40 
41 /**
42  * Is it a character (IsAlphaCore() and underscore)
43  *
44  * \param  ch The character
45  * \return True if ch is a character, False otherwise
46  */
IsAlpha(int ch)47 static inline bool IsAlpha(int ch) {
48     return (IsAlphaCore(ch) || ch == '_');
49 }
50 
51 /**
52  * Is it a symbolic character (IsAlpha() and colon)
53  *
54  * \param  ch The character
55  * \return True if ch is a character, False otherwise
56  */
IsAlphaSym(int ch)57 static inline bool IsAlphaSym(int ch) {
58     return (IsAlpha(ch) || ch == ':');
59 }
60 
61 /**
62  * Is it a numerical character (IsAlpha() and 0 - 9)
63  *
64  * \param  ch The character
65  * \return True if ch is a character, False otherwise
66  */
IsAlNum(int ch)67 static inline bool IsAlNum(int ch) {
68     return ((ch >= '0' && ch <= '9') || IsAlpha(ch));
69 }
70 
71 /**
72  * Is it a symbolic numerical character (IsAlNum() and colon)
73  *
74  * \param  ch The character
75  * \return True if ch is a character, False otherwise
76  */
IsAlNumSym(int ch)77 static inline bool IsAlNumSym(int ch) {
78     return (IsAlNum(ch) || ch == ':');
79 }
80 
81 /**
82  * The lexer function
83  *
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
89  */
ColouriseMagikDoc(unsigned int startPos,int length,int initStyle,WordList * keywordlists[],Accessor & styler)90 static void ColouriseMagikDoc(unsigned int startPos, int length, int initStyle,
91                            WordList *keywordlists[], Accessor &styler) {
92     styler.StartAt(startPos);
93 
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];
99 
100 	StyleContext sc(startPos, length, initStyle, styler);
101 
102 
103 	for (; sc.More(); sc.Forward()) {
104 
105     repeat:
106 
107         if(sc.ch == '#') {
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);
112             goto repeat;
113         }
114 
115         if(sc.ch == '"') {
116             sc.SetState(SCE_MAGIK_STRING);
117 
118             if(sc.More())
119             {
120                 sc.Forward();
121                 for(; sc.More() && sc.ch != '"'; sc.Forward());
122             }
123 
124             sc.ForwardSetState(SCE_MAGIK_DEFAULT);
125             goto repeat;
126         }
127 
128 	    // The default state
129 	    if(sc.state == SCE_MAGIK_DEFAULT) {
130 
131 	        // A certain keyword has been detected
132 	        if (sc.ch == '_' && (
133                     sc.currentPos == 0 || !IsAlNum(sc.chPrev))) {
134 	            char keyword[50];
135 	            memset(keyword, '\0', 50);
136 
137 	            for(
138                     int scanPosition = 0;
139                     scanPosition < 50;
140                     scanPosition++) {
141 	                char keywordChar = static_cast<char>(
142                         tolower(styler.SafeGetCharAt(
143                             scanPosition +
144                                 static_cast<int>(sc.currentPos+1), ' ')));
145                     if(IsAlpha(keywordChar)) {
146                         keyword[scanPosition] = keywordChar;
147                     } else {
148                         break;
149                     }
150 	            }
151 
152                 // It is a pragma
153 	            if(pragmatics.InList(keyword)) {
154 	                sc.SetState(SCE_MAGIK_PRAGMA);
155 	            }
156 
157 	            // it is a normal keyword like _local, _self, etc.
158 	            else if(keywords.InList(keyword)) {
159 	                sc.SetState(SCE_MAGIK_KEYWORD);
160 	            }
161 
162                 // It is a container keyword, such as _method, _proc, etc.
163 	            else if(containers.InList(keyword)) {
164 	                sc.SetState(SCE_MAGIK_CONTAINER);
165 	            }
166 
167 	            // It is a flow keyword, such as _for, _if, _try, etc.
168 	            else if(flow.InList(keyword)) {
169 	                sc.SetState(SCE_MAGIK_FLOW);
170 	            }
171 
172 	            // Interpret as unknown keyword
173 	            else {
174 	                sc.SetState(SCE_MAGIK_UNKNOWN_KEYWORD);
175 	            }
176 	        }
177 
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 == '|') {
186 	                    for(sc.Forward();
187                             sc.More() && sc.ch != '|';
188                             sc.Forward());
189 	                }
190 	                else break;
191 
192 	                firstTrip = false;
193 	            }
194 	            sc.SetState(SCE_MAGIK_DEFAULT);
195 	            goto repeat;
196 	        }
197 
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)) {
204 	                    firstTrip = false;
205 	                }
206 	                else if(!firstTrip && IsAlpha(sc.ch));
207 	                else break;
208 	            }
209 	            sc.SetState(SCE_MAGIK_DEFAULT);
210 	            goto repeat;
211 	        }
212 
213 	        // Start of a character
214             else if(sc.ch == '%') {
215                 sc.SetState(SCE_MAGIK_CHARACTER);
216                 sc.Forward();
217                 char keyword[50];
218 	            memset(keyword, '\0', 50);
219 
220 	            for(
221                     int scanPosition = 0;
222                     scanPosition < 50;
223                     scanPosition++) {
224 	                char keywordChar = static_cast<char>(
225                         tolower(styler.SafeGetCharAt(
226                             scanPosition +
227                                 static_cast<int>(sc.currentPos), ' ')));
228                     if(IsAlpha(keywordChar)) {
229                         keyword[scanPosition] = keywordChar;
230                     } else {
231                         break;
232                     }
233 	            }
234 
235 	            if(characters.InList(keyword)) {
236 	                sc.Forward(static_cast<int>(strlen(keyword)));
237 	            } else {
238 	                sc.Forward();
239 	            }
240 
241                 sc.SetState(SCE_MAGIK_DEFAULT);
242                 goto repeat;
243             }
244 
245             // Operators
246 	        else if(
247                 sc.ch == '>' ||
248                 sc.ch == '<' ||
249                 sc.ch == '.' ||
250                 sc.ch == ',' ||
251                 sc.ch == '+' ||
252                 sc.ch == '-' ||
253                 sc.ch == '/' ||
254                 sc.ch == '*' ||
255                 sc.ch == '~' ||
256                 sc.ch == '$' ||
257                 sc.ch == '=') {
258                 sc.SetState(SCE_MAGIK_OPERATOR);
259             }
260 
261             // Braces
262             else if(sc.ch == '(' || sc.ch == ')') {
263                 sc.SetState(SCE_MAGIK_BRACE_BLOCK);
264             }
265 
266             // Brackets
267             else if(sc.ch == '{' || sc.ch == '}') {
268                 sc.SetState(SCE_MAGIK_BRACKET_BLOCK);
269             }
270 
271             // Square Brackets
272             else if(sc.ch == '[' || sc.ch == ']') {
273                 sc.SetState(SCE_MAGIK_SQBRACKET_BLOCK);
274             }
275 
276 
277 	    }
278 
279 	    // It is an operator
280 	    else if(
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);
286 	        goto repeat;
287 	    }
288 
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);
293                 goto repeat;
294 	        }
295 	    }
296 
297 	    // It is the keyword state
298 	    else if(
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);
305 	            goto repeat;
306 	        }
307 	    }
308 	}
309 
310 	sc.Complete();
311 }
312 
313 /**
314  * The word list description
315  */
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)",
323     0};
324 
325 /**
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).
330  *
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
335  *         0 otherwise
336  */
IsFoldingContainer(WordList & keywordslist,char * keyword)337 static inline int IsFoldingContainer(WordList &keywordslist, char * keyword) {
338     if(
339         strlen(keyword) > 3 &&
340         keyword[0] == 'e' && keyword[1] == 'n' && keyword[2] == 'd') {
341         if (keywordslist.InList(keyword + 3)) {
342             return -1;
343         }
344 
345     } else {
346         if(keywordslist.InList(keyword)) {
347             return 1;
348         }
349     }
350 
351     return 0;
352 }
353 
354 /**
355  * The folding function
356  *
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
361  */
FoldMagikDoc(unsigned int startPos,int length,int,WordList * keywordslists[],Accessor & styler)362 static void FoldMagikDoc(unsigned int startPos, int length, int,
363     WordList *keywordslists[], Accessor &styler) {
364 
365     bool compact = styler.GetPropertyInt("fold.compact") != 0;
366 
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;
372 
373     for(
374         int currentPos = startPos;
375         currentPos < endPos;
376         currentPos++) {
377             char currentState = styler.StyleAt(currentPos);
378             char c = styler.SafeGetCharAt(currentPos, ' ');
379             int prevLine = styler.GetLine(currentPos - 1);
380             line = styler.GetLine(currentPos);
381 
382             // Default situation
383             if(prevLine < line) {
384                 styler.SetLevel(line, (level|flags) & ~SC_FOLDLEVELHEADERFLAG);
385                 flags = styler.LevelAt(line) & ~SC_FOLDLEVELNUMBERMASK;
386             }
387 
388             if(
389                 (
390                     currentState == SCE_MAGIK_CONTAINER ||
391                     currentState == SCE_MAGIK_FLOW
392                 ) &&
393                 c == '_') {
394 
395                 char keyword[50];
396                 memset(keyword, '\0', 50);
397 
398                 for(
399                     int scanPosition = 0;
400                     scanPosition < 50;
401                     scanPosition++) {
402                     char keywordChar = static_cast<char>(
403                         tolower(styler.SafeGetCharAt(
404                             scanPosition +
405                                 currentPos + 1, ' ')));
406                     if(IsAlpha(keywordChar)) {
407                         keyword[scanPosition] = keywordChar;
408                     } else {
409                         break;
410                     }
411                 }
412 
413                 if(IsFoldingContainer(foldingElements, keyword) > 0) {
414                     styler.SetLevel(
415                         line,
416                         styler.LevelAt(line) | SC_FOLDLEVELHEADERFLAG);
417                     level++;
418                 } else if(IsFoldingContainer(foldingElements, keyword) < 0) {
419                     styler.SetLevel(line, styler.LevelAt(line));
420                     level--;
421                 }
422             }
423 
424             if(
425                 compact && (
426                     currentState == SCE_MAGIK_BRACE_BLOCK ||
427                     currentState == SCE_MAGIK_BRACKET_BLOCK ||
428                     currentState == SCE_MAGIK_SQBRACKET_BLOCK)) {
429                 if(c == '{' || c == '[' || c == '(') {
430                     styler.SetLevel(
431                         line,
432                         styler.LevelAt(line) | SC_FOLDLEVELHEADERFLAG);
433                     level++;
434                 } else if(c == '}' || c == ']' || c == ')') {
435                     styler.SetLevel(line, styler.LevelAt(line));
436                     level--;
437                 }
438             }
439         }
440 
441 }
442 
443 /**
444  * Injecting the module
445  */
446 LexerModule lmMagikSF(
447     SCLEX_MAGIK, ColouriseMagikDoc, "magiksf", FoldMagikDoc, magikWordListDesc);
448 
449