]>
Commit | Line | Data |
---|---|---|
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 | */ | |
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 | */ | |
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 | */ | |
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 | */ | |
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 | */ | |
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 | */ | |
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 | */ | |
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 | */ | |
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 |