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