]> git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/lexers/LexAbaqus.cxx
Allow unsetting wxMenuItem as start of radio group too.
[wxWidgets.git] / src / stc / scintilla / lexers / LexAbaqus.cxx
1 // Scintilla source code edit control
2 /** @file LexABAQUS.cxx
3 ** Lexer for ABAQUS. Based on the lexer for APDL by Hadar Raz.
4 ** By Sergio Lucato.
5 ** Sort of completely rewritten by Gertjan Kloosterman
6 **/
7 // The License.txt file describes the conditions under which this software may be distributed.
8
9 // Code folding copyied and modified from LexBasic.cxx
10
11 #include <stdlib.h>
12 #include <string.h>
13 #include <stdio.h>
14 #include <stdarg.h>
15 #include <assert.h>
16 #include <ctype.h>
17
18 #include "ILexer.h"
19 #include "Scintilla.h"
20 #include "SciLexer.h"
21
22 #include "WordList.h"
23 #include "LexAccessor.h"
24 #include "Accessor.h"
25 #include "StyleContext.h"
26 #include "CharacterSet.h"
27 #include "LexerModule.h"
28
29 #ifdef SCI_NAMESPACE
30 using namespace Scintilla;
31 #endif
32
33 static inline bool IsAWordChar(const int ch) {
34 return (ch < 0x80 && (isalnum(ch) || (ch == '_')));
35 }
36
37 static inline bool IsAKeywordChar(const int ch) {
38 return (ch < 0x80 && (isalnum(ch) || (ch == '_') || (ch == ' ')));
39 }
40
41 static inline bool IsASetChar(const int ch) {
42 return (ch < 0x80 && (isalnum(ch) || (ch == '_') || (ch == '.') || (ch == '-')));
43 }
44
45 static inline bool IsAnOperator(char ch) {
46 // '.' left out as it is used to make up numbers
47 if (ch == '*' || ch == '/' || ch == '-' || ch == '+' ||
48 ch == '(' || ch == ')' || ch == '=' || ch == '^' ||
49 ch == '[' || ch == ']' || ch == '<' || ch == '&' ||
50 ch == '>' || ch == ',' || ch == '|' || ch == '~' ||
51 ch == '$' || ch == ':' || ch == '%')
52 return true;
53 return false;
54 }
55
56 static void ColouriseABAQUSDoc(unsigned int startPos, int length, int initStyle, WordList*[] /* *keywordlists[] */,
57 Accessor &styler) {
58 enum localState { KW_LINE_KW, KW_LINE_COMMA, KW_LINE_PAR, KW_LINE_EQ, KW_LINE_VAL, \
59 DAT_LINE_VAL, DAT_LINE_COMMA,\
60 COMMENT_LINE,\
61 ST_ERROR, LINE_END } state ;
62
63 // Do not leak onto next line
64 state = LINE_END ;
65 initStyle = SCE_ABAQUS_DEFAULT;
66 StyleContext sc(startPos, length, initStyle, styler);
67
68 // Things are actually quite simple
69 // we have commentlines
70 // keywordlines and datalines
71 // On a data line there will only be colouring of numbers
72 // a keyword line is constructed as
73 // *word,[ paramname[=paramvalue]]*
74 // if the line ends with a , the keyword line continues onto the new line
75
76 for (; sc.More(); sc.Forward()) {
77 switch ( state ) {
78 case KW_LINE_KW :
79 if ( sc.atLineEnd ) {
80 // finished the line in keyword state, switch to LINE_END
81 sc.SetState(SCE_ABAQUS_DEFAULT) ;
82 state = LINE_END ;
83 } else if ( IsAKeywordChar(sc.ch) ) {
84 // nothing changes
85 state = KW_LINE_KW ;
86 } else if ( sc.ch == ',' ) {
87 // Well well we say a comma, arguments *MUST* follow
88 sc.SetState(SCE_ABAQUS_OPERATOR) ;
89 state = KW_LINE_COMMA ;
90 } else {
91 // Flag an error
92 sc.SetState(SCE_ABAQUS_PROCESSOR) ;
93 state = ST_ERROR ;
94 }
95 // Done with processing
96 break ;
97 case KW_LINE_COMMA :
98 // acomma on a keywordline was seen
99 if ( IsAKeywordChar(sc.ch)) {
100 sc.SetState(SCE_ABAQUS_ARGUMENT) ;
101 state = KW_LINE_PAR ;
102 } else if ( sc.atLineEnd || (sc.ch == ',') ) {
103 // we remain in keyword mode
104 state = KW_LINE_COMMA ;
105 } else if ( sc.ch == ' ' ) {
106 sc.SetState(SCE_ABAQUS_DEFAULT) ;
107 state = KW_LINE_COMMA ;
108 } else {
109 // Anything else constitutes an error
110 sc.SetState(SCE_ABAQUS_PROCESSOR) ;
111 state = ST_ERROR ;
112 }
113 break ;
114 case KW_LINE_PAR :
115 if ( sc.atLineEnd ) {
116 sc.SetState(SCE_ABAQUS_DEFAULT) ;
117 state = LINE_END ;
118 } else if ( IsAKeywordChar(sc.ch) || (sc.ch == '-') ) {
119 // remain in this state
120 state = KW_LINE_PAR ;
121 } else if ( sc.ch == ',' ) {
122 sc.SetState(SCE_ABAQUS_OPERATOR) ;
123 state = KW_LINE_COMMA ;
124 } else if ( sc.ch == '=' ) {
125 sc.SetState(SCE_ABAQUS_OPERATOR) ;
126 state = KW_LINE_EQ ;
127 } else {
128 // Anything else constitutes an error
129 sc.SetState(SCE_ABAQUS_PROCESSOR) ;
130 state = ST_ERROR ;
131 }
132 break ;
133 case KW_LINE_EQ :
134 if ( sc.ch == ' ' ) {
135 sc.SetState(SCE_ABAQUS_DEFAULT) ;
136 // remain in this state
137 state = KW_LINE_EQ ;
138 } else if ( IsADigit(sc.ch) || (sc.ch == '-') || (sc.ch == '.' && IsADigit(sc.chNext)) ) {
139 sc.SetState(SCE_ABAQUS_NUMBER) ;
140 state = KW_LINE_VAL ;
141 } else if ( IsAKeywordChar(sc.ch) ) {
142 sc.SetState(SCE_ABAQUS_DEFAULT) ;
143 state = KW_LINE_VAL ;
144 } else if ( (sc.ch == '\'') || (sc.ch == '\"') ) {
145 sc.SetState(SCE_ABAQUS_STRING) ;
146 state = KW_LINE_VAL ;
147 } else {
148 sc.SetState(SCE_ABAQUS_PROCESSOR) ;
149 state = ST_ERROR ;
150 }
151 break ;
152 case KW_LINE_VAL :
153 if ( sc.atLineEnd ) {
154 sc.SetState(SCE_ABAQUS_DEFAULT) ;
155 state = LINE_END ;
156 } else if ( IsASetChar(sc.ch) && (sc.state == SCE_ABAQUS_DEFAULT) ) {
157 // nothing changes
158 state = KW_LINE_VAL ;
159 } else if (( (IsADigit(sc.ch) || sc.ch == '.' || (sc.ch == 'e' || sc.ch == 'E') ||
160 ((sc.ch == '+' || sc.ch == '-') && (sc.chPrev == 'e' || sc.chPrev == 'E')))) &&
161 (sc.state == SCE_ABAQUS_NUMBER)) {
162 // remain in number mode
163 state = KW_LINE_VAL ;
164 } else if (sc.state == SCE_ABAQUS_STRING) {
165 // accept everything until a closing quote
166 if ( sc.ch == '\'' || sc.ch == '\"' ) {
167 sc.SetState(SCE_ABAQUS_DEFAULT) ;
168 state = KW_LINE_VAL ;
169 }
170 } else if ( sc.ch == ',' ) {
171 sc.SetState(SCE_ABAQUS_OPERATOR) ;
172 state = KW_LINE_COMMA ;
173 } else {
174 // anything else is an error
175 sc.SetState(SCE_ABAQUS_PROCESSOR) ;
176 state = ST_ERROR ;
177 }
178 break ;
179 case DAT_LINE_VAL :
180 if ( sc.atLineEnd ) {
181 sc.SetState(SCE_ABAQUS_DEFAULT) ;
182 state = LINE_END ;
183 } else if ( IsASetChar(sc.ch) && (sc.state == SCE_ABAQUS_DEFAULT) ) {
184 // nothing changes
185 state = DAT_LINE_VAL ;
186 } else if (( (IsADigit(sc.ch) || sc.ch == '.' || (sc.ch == 'e' || sc.ch == 'E') ||
187 ((sc.ch == '+' || sc.ch == '-') && (sc.chPrev == 'e' || sc.chPrev == 'E')))) &&
188 (sc.state == SCE_ABAQUS_NUMBER)) {
189 // remain in number mode
190 state = DAT_LINE_VAL ;
191 } else if (sc.state == SCE_ABAQUS_STRING) {
192 // accept everything until a closing quote
193 if ( sc.ch == '\'' || sc.ch == '\"' ) {
194 sc.SetState(SCE_ABAQUS_DEFAULT) ;
195 state = DAT_LINE_VAL ;
196 }
197 } else if ( sc.ch == ',' ) {
198 sc.SetState(SCE_ABAQUS_OPERATOR) ;
199 state = DAT_LINE_COMMA ;
200 } else {
201 // anything else is an error
202 sc.SetState(SCE_ABAQUS_PROCESSOR) ;
203 state = ST_ERROR ;
204 }
205 break ;
206 case DAT_LINE_COMMA :
207 // a comma on a data line was seen
208 if ( sc.atLineEnd ) {
209 sc.SetState(SCE_ABAQUS_DEFAULT) ;
210 state = LINE_END ;
211 } else if ( sc.ch == ' ' ) {
212 sc.SetState(SCE_ABAQUS_DEFAULT) ;
213 state = DAT_LINE_COMMA ;
214 } else if (sc.ch == ',') {
215 sc.SetState(SCE_ABAQUS_OPERATOR) ;
216 state = DAT_LINE_COMMA ;
217 } else if ( IsADigit(sc.ch) || (sc.ch == '-')|| (sc.ch == '.' && IsADigit(sc.chNext)) ) {
218 sc.SetState(SCE_ABAQUS_NUMBER) ;
219 state = DAT_LINE_VAL ;
220 } else if ( IsAKeywordChar(sc.ch) ) {
221 sc.SetState(SCE_ABAQUS_DEFAULT) ;
222 state = DAT_LINE_VAL ;
223 } else if ( (sc.ch == '\'') || (sc.ch == '\"') ) {
224 sc.SetState(SCE_ABAQUS_STRING) ;
225 state = DAT_LINE_VAL ;
226 } else {
227 sc.SetState(SCE_ABAQUS_PROCESSOR) ;
228 state = ST_ERROR ;
229 }
230 break ;
231 case COMMENT_LINE :
232 if ( sc.atLineEnd ) {
233 sc.SetState(SCE_ABAQUS_DEFAULT) ;
234 state = LINE_END ;
235 }
236 break ;
237 case ST_ERROR :
238 if ( sc.atLineEnd ) {
239 sc.SetState(SCE_ABAQUS_DEFAULT) ;
240 state = LINE_END ;
241 }
242 break ;
243 case LINE_END :
244 if ( sc.atLineEnd || sc.ch == ' ' ) {
245 // nothing changes
246 state = LINE_END ;
247 } else if ( sc.ch == '*' ) {
248 if ( sc.chNext == '*' ) {
249 state = COMMENT_LINE ;
250 sc.SetState(SCE_ABAQUS_COMMENT) ;
251 } else {
252 state = KW_LINE_KW ;
253 sc.SetState(SCE_ABAQUS_STARCOMMAND) ;
254 }
255 } else {
256 // it must be a data line, things are as if we are in DAT_LINE_COMMA
257 if ( sc.ch == ',' ) {
258 sc.SetState(SCE_ABAQUS_OPERATOR) ;
259 state = DAT_LINE_COMMA ;
260 } else if ( IsADigit(sc.ch) || (sc.ch == '-')|| (sc.ch == '.' && IsADigit(sc.chNext)) ) {
261 sc.SetState(SCE_ABAQUS_NUMBER) ;
262 state = DAT_LINE_VAL ;
263 } else if ( IsAKeywordChar(sc.ch) ) {
264 sc.SetState(SCE_ABAQUS_DEFAULT) ;
265 state = DAT_LINE_VAL ;
266 } else if ( (sc.ch == '\'') || (sc.ch == '\"') ) {
267 sc.SetState(SCE_ABAQUS_STRING) ;
268 state = DAT_LINE_VAL ;
269 } else {
270 sc.SetState(SCE_ABAQUS_PROCESSOR) ;
271 state = ST_ERROR ;
272 }
273 }
274 break ;
275 }
276 }
277 sc.Complete();
278 }
279
280 //------------------------------------------------------------------------------
281 // This copyied and modified from LexBasic.cxx
282 //------------------------------------------------------------------------------
283
284 /* Bits:
285 * 1 - whitespace
286 * 2 - operator
287 * 4 - identifier
288 * 8 - decimal digit
289 * 16 - hex digit
290 * 32 - bin digit
291 */
292 static int character_classification[128] =
293 {
294 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
295 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
296 1, 2, 0, 2, 2, 2, 2, 2, 2, 2, 6, 2, 2, 2, 10, 6,
297 60, 60, 28, 28, 28, 28, 28, 28, 28, 28, 2, 2, 2, 2, 2, 2,
298 2, 20, 20, 20, 20, 20, 20, 4, 4, 4, 4, 4, 4, 4, 4, 4,
299 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 4,
300 2, 20, 20, 20, 20, 20, 20, 4, 4, 4, 4, 4, 4, 4, 4, 4,
301 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 0
302 };
303
304 static bool IsSpace(int c) {
305 return c < 128 && (character_classification[c] & 1);
306 }
307
308 static bool IsIdentifier(int c) {
309 return c < 128 && (character_classification[c] & 4);
310 }
311
312 static int LowerCase(int c)
313 {
314 if (c >= 'A' && c <= 'Z')
315 return 'a' + c - 'A';
316 return c;
317 }
318
319 static int LineEnd(int line, Accessor &styler)
320 {
321 const int docLines = styler.GetLine(styler.Length() - 1); // Available last line
322 int eol_pos ;
323 // if the line is the last line, the eol_pos is styler.Length()
324 // eol will contain a new line, or a virtual new line
325 if ( docLines == line )
326 eol_pos = styler.Length() ;
327 else
328 eol_pos = styler.LineStart(line + 1) - 1;
329 return eol_pos ;
330 }
331
332 static int LineStart(int line, Accessor &styler)
333 {
334 return styler.LineStart(line) ;
335 }
336
337 // LineType
338 //
339 // bits determines the line type
340 // 1 : data line
341 // 2 : only whitespace
342 // 3 : data line with only whitespace
343 // 4 : keyword line
344 // 5 : block open keyword line
345 // 6 : block close keyword line
346 // 7 : keyword line in error
347 // 8 : comment line
348 static int LineType(int line, Accessor &styler) {
349 int pos = LineStart(line, styler) ;
350 int eol_pos = LineEnd(line, styler) ;
351
352 int c ;
353 char ch = ' ';
354
355 int i = pos ;
356 while ( i < eol_pos ) {
357 c = styler.SafeGetCharAt(i);
358 ch = static_cast<char>(LowerCase(c));
359 // We can say something as soon as no whitespace
360 // was encountered
361 if ( !IsSpace(c) )
362 break ;
363 i++ ;
364 }
365
366 if ( i >= eol_pos ) {
367 // This is a whitespace line, currently
368 // classifies as data line
369 return 3 ;
370 }
371
372 if ( ch != '*' ) {
373 // This is a data line
374 return 1 ;
375 }
376
377 if ( i == eol_pos - 1 ) {
378 // Only a single *, error but make keyword line
379 return 4+3 ;
380 }
381
382 // This means we can have a second character
383 // if that is also a * this means a comment
384 // otherwise it is a keyword.
385 c = styler.SafeGetCharAt(i+1);
386 ch = static_cast<char>(LowerCase(c));
387 if ( ch == '*' ) {
388 return 8 ;
389 }
390
391 // At this point we know this is a keyword line
392 // the character at position i is a *
393 // it is not a comment line
394 char word[256] ;
395 int wlen = 0;
396
397 word[wlen] = '*' ;
398 wlen++ ;
399
400 i++ ;
401 while ( (i < eol_pos) && (wlen < 255) ) {
402 c = styler.SafeGetCharAt(i);
403 ch = static_cast<char>(LowerCase(c));
404
405 if ( (!IsSpace(c)) && (!IsIdentifier(c)) )
406 break ;
407
408 if ( IsIdentifier(c) ) {
409 word[wlen] = ch ;
410 wlen++ ;
411 }
412
413 i++ ;
414 }
415
416 word[wlen] = 0 ;
417
418 // Make a comparison
419 if ( !strcmp(word, "*step") ||
420 !strcmp(word, "*part") ||
421 !strcmp(word, "*instance") ||
422 !strcmp(word, "*assembly")) {
423 return 4+1 ;
424 }
425
426 if ( !strcmp(word, "*endstep") ||
427 !strcmp(word, "*endpart") ||
428 !strcmp(word, "*endinstance") ||
429 !strcmp(word, "*endassembly")) {
430 return 4+2 ;
431 }
432
433 return 4 ;
434 }
435
436 static void SafeSetLevel(int line, int level, Accessor &styler)
437 {
438 if ( line < 0 )
439 return ;
440
441 int mask = ((~SC_FOLDLEVELHEADERFLAG) | (~SC_FOLDLEVELWHITEFLAG));
442
443 if ( (level & mask) < 0 )
444 return ;
445
446 if ( styler.LevelAt(line) != level )
447 styler.SetLevel(line, level) ;
448 }
449
450 static void FoldABAQUSDoc(unsigned int startPos, int length, int,
451 WordList *[], Accessor &styler) {
452 int startLine = styler.GetLine(startPos) ;
453 int endLine = styler.GetLine(startPos+length-1) ;
454
455 // bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
456 // We want to deal with all the cases
457 // To know the correct indentlevel, we need to look back to the
458 // previous command line indentation level
459 // order of formatting keyline datalines commentlines
460 int beginData = -1 ;
461 int beginComment = -1 ;
462 int prvKeyLine = startLine ;
463 int prvKeyLineTp = 0 ;
464
465 // Scan until we find the previous keyword line
466 // this will give us the level reference that we need
467 while ( prvKeyLine > 0 ) {
468 prvKeyLine-- ;
469 prvKeyLineTp = LineType(prvKeyLine, styler) ;
470 if ( prvKeyLineTp & 4 )
471 break ;
472 }
473
474 // Determine the base line level of all lines following
475 // the previous keyword
476 // new keyword lines are placed on this level
477 //if ( prvKeyLineTp & 4 ) {
478 int level = styler.LevelAt(prvKeyLine) & ~SC_FOLDLEVELHEADERFLAG ;
479 //}
480
481 // uncomment line below if weird behaviour continues
482 prvKeyLine = -1 ;
483
484 // Now start scanning over the lines.
485 for ( int line = startLine; line <= endLine; line++ ) {
486 int lineType = LineType(line, styler) ;
487
488 // Check for comment line
489 if ( lineType == 8 ) {
490 if ( beginComment < 0 ) {
491 beginComment = line ;
492 }
493 }
494
495 // Check for data line
496 if ( (lineType == 1) || (lineType == 3) ) {
497 if ( beginData < 0 ) {
498 if ( beginComment >= 0 ) {
499 beginData = beginComment ;
500 } else {
501 beginData = line ;
502 }
503 }
504 beginComment = -1 ;
505 }
506
507 // Check for keywordline.
508 // As soon as a keyword line is encountered, we can set the
509 // levels of everything from the previous keyword line to this one
510 if ( lineType & 4 ) {
511 // this is a keyword, we can now place the previous keyword
512 // all its data lines and the remainder
513
514 // Write comments and data line
515 if ( beginComment < 0 ) {
516 beginComment = line ;
517 }
518
519 if ( beginData < 0 ) {
520 beginData = beginComment ;
521 if ( prvKeyLineTp != 5 )
522 SafeSetLevel(prvKeyLine, level, styler) ;
523 else
524 SafeSetLevel(prvKeyLine, level | SC_FOLDLEVELHEADERFLAG, styler) ;
525 } else {
526 SafeSetLevel(prvKeyLine, level | SC_FOLDLEVELHEADERFLAG, styler) ;
527 }
528
529 int datLevel = level + 1 ;
530 if ( !(prvKeyLineTp & 4) ) {
531 datLevel = level ;
532 }
533
534 for ( int ll = beginData; ll < beginComment; ll++ )
535 SafeSetLevel(ll, datLevel, styler) ;
536
537 // The keyword we just found is going to be written at another level
538 // if we have a type 5 and type 6
539 if ( prvKeyLineTp == 5 ) {
540 level += 1 ;
541 }
542
543 if ( prvKeyLineTp == 6 ) {
544 level -= 1 ;
545 if ( level < 0 ) {
546 level = 0 ;
547 }
548 }
549
550 for ( int lll = beginComment; lll < line; lll++ )
551 SafeSetLevel(lll, level, styler) ;
552
553 // wrap and reset
554 beginComment = -1 ;
555 beginData = -1 ;
556 prvKeyLine = line ;
557 prvKeyLineTp = lineType ;
558 }
559
560 }
561
562 if ( beginComment < 0 ) {
563 beginComment = endLine + 1 ;
564 } else {
565 // We need to find out whether this comment block is followed by
566 // a data line or a keyword line
567 const int docLines = styler.GetLine(styler.Length() - 1);
568
569 for ( int line = endLine + 1; line <= docLines; line++ ) {
570 int lineType = LineType(line, styler) ;
571
572 if ( lineType != 8 ) {
573 if ( !(lineType & 4) ) {
574 beginComment = endLine + 1 ;
575 }
576 break ;
577 }
578 }
579 }
580
581 if ( beginData < 0 ) {
582 beginData = beginComment ;
583 if ( prvKeyLineTp != 5 )
584 SafeSetLevel(prvKeyLine, level, styler) ;
585 else
586 SafeSetLevel(prvKeyLine, level | SC_FOLDLEVELHEADERFLAG, styler) ;
587 } else {
588 SafeSetLevel(prvKeyLine, level | SC_FOLDLEVELHEADERFLAG, styler) ;
589 }
590
591 int datLevel = level + 1 ;
592 if ( !(prvKeyLineTp & 4) ) {
593 datLevel = level ;
594 }
595
596 for ( int ll = beginData; ll < beginComment; ll++ )
597 SafeSetLevel(ll, datLevel, styler) ;
598
599 if ( prvKeyLineTp == 5 ) {
600 level += 1 ;
601 }
602
603 if ( prvKeyLineTp == 6 ) {
604 level -= 1 ;
605 }
606 for ( int m = beginComment; m <= endLine; m++ )
607 SafeSetLevel(m, level, styler) ;
608 }
609
610 static const char * const abaqusWordListDesc[] = {
611 "processors",
612 "commands",
613 "slashommands",
614 "starcommands",
615 "arguments",
616 "functions",
617 0
618 };
619
620 LexerModule lmAbaqus(SCLEX_ABAQUS, ColouriseABAQUSDoc, "abaqus", FoldABAQUSDoc, abaqusWordListDesc);