1 // Scintilla source code edit control
2 // Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
3 // The License.txt file describes the conditions under which this software may be distributed.
4 /** @file LexErlang.cxx
6 ** Enhanced by Etienne 'Lenain' Girondel (lenaing@gmail.com)
7 ** Originally wrote by Peter-Henry Mander,
8 ** based on Matlab lexer by José Fonseca.
20 #include "StyleContext.h"
22 #include "Scintilla.h"
26 using namespace Scintilla
;
29 static int is_radix(int radix
, int ch
) {
32 if (36 < radix
|| 2 > radix
)
37 } else if (isalnum(ch
)) {
38 digit
= toupper(ch
) - 'A' + 10;
43 return (digit
< radix
);
70 static inline bool IsAWordChar(const int ch
) {
71 return (ch
< 0x80) && (ch
!= ' ') && (isalnum(ch
) || ch
== '_');
74 static void ColouriseErlangDoc(unsigned int startPos
, int length
, int initStyle
,
75 WordList
*keywordlists
[], Accessor
&styler
) {
77 StyleContext
sc(startPos
, length
, initStyle
, styler
);
78 WordList
&reservedWords
= *keywordlists
[0];
79 WordList
&erlangBIFs
= *keywordlists
[1];
80 WordList
&erlangPreproc
= *keywordlists
[2];
81 WordList
&erlangModulesAtt
= *keywordlists
[3];
82 WordList
&erlangDoc
= *keywordlists
[4];
83 WordList
&erlangDocMacro
= *keywordlists
[5];
85 int exponent_digits
= 0;
86 atom_parse_state_t parse_state
= STATE_NULL
;
87 atom_parse_state_t old_parse_state
= STATE_NULL
;
88 bool to_late_to_comment
= false;
90 int old_style
= SCE_ERLANG_DEFAULT
;
92 styler
.StartAt(startPos
);
94 for (; sc
.More(); sc
.Forward()) {
95 int style
= SCE_ERLANG_DEFAULT
;
96 if (STATE_NULL
!= parse_state
) {
98 switch (parse_state
) {
100 case STATE_NULL
: sc
.SetState(SCE_ERLANG_DEFAULT
); break;
102 /* COMMENTS ------------------------------------------------------*/
105 to_late_to_comment
= true;
106 } else if (!to_late_to_comment
&& sc
.ch
== '%') {
107 // Switch to comment level 2 (Function)
108 sc
.ChangeState(SCE_ERLANG_COMMENT_FUNCTION
);
109 old_style
= SCE_ERLANG_COMMENT_FUNCTION
;
110 parse_state
= COMMENT_FUNCTION
;
114 // V--- Falling through!
115 case COMMENT_FUNCTION
: {
117 to_late_to_comment
= true;
118 } else if (!to_late_to_comment
&& sc
.ch
== '%') {
119 // Switch to comment level 3 (Module)
120 sc
.ChangeState(SCE_ERLANG_COMMENT_MODULE
);
121 old_style
= SCE_ERLANG_COMMENT_MODULE
;
122 parse_state
= COMMENT_MODULE
;
126 // V--- Falling through!
127 case COMMENT_MODULE
: {
128 if (parse_state
!= COMMENT
) {
129 // Search for comment documentation
130 if (sc
.chNext
== '@') {
131 old_parse_state
= parse_state
;
132 parse_state
= ('{' == sc
.ch
)
135 sc
.ForwardSetState(sc
.state
);
139 // All comments types fall here.
141 to_late_to_comment
= false;
142 sc
.SetState(SCE_ERLANG_DEFAULT
);
143 parse_state
= STATE_NULL
;
148 // V--- Falling through!
149 case COMMENT_DOC_MACRO
: {
151 if (!isalnum(sc
.ch
)) {
152 // Try to match documentation comment
153 sc
.GetCurrent(cur
, sizeof(cur
));
155 if (parse_state
== COMMENT_DOC_MACRO
156 && erlangDocMacro
.InList(cur
)) {
157 sc
.ChangeState(SCE_ERLANG_COMMENT_DOC_MACRO
);
158 while (sc
.ch
!= '}' && !sc
.atLineEnd
)
160 } else if (erlangDoc
.InList(cur
)) {
161 sc
.ChangeState(SCE_ERLANG_COMMENT_DOC
);
163 sc
.ChangeState(old_style
);
166 // Switch back to old state
167 sc
.SetState(old_style
);
168 parse_state
= old_parse_state
;
172 to_late_to_comment
= false;
173 sc
.ChangeState(old_style
);
174 sc
.SetState(SCE_ERLANG_DEFAULT
);
175 parse_state
= STATE_NULL
;
179 /* -------------------------------------------------------------- */
180 /* Atoms ---------------------------------------------------------*/
181 case ATOM_UNQUOTED
: {
183 parse_state
= NODE_NAME_UNQUOTED
;
184 } else if (sc
.ch
== ':') {
185 // Searching for module name
186 if (sc
.chNext
== ' ') {
188 sc
.ChangeState(SCE_ERLANG_UNKNOWN
);
189 parse_state
= STATE_NULL
;
192 if (isalnum(sc
.ch
)) {
193 sc
.GetCurrent(cur
, sizeof(cur
));
194 sc
.ChangeState(SCE_ERLANG_MODULES
);
195 sc
.SetState(SCE_ERLANG_MODULES
);
198 } else if (!IsAWordChar(sc
.ch
)) {
200 sc
.GetCurrent(cur
, sizeof(cur
));
201 if (reservedWords
.InList(cur
)) {
202 style
= SCE_ERLANG_KEYWORD
;
203 } else if (erlangBIFs
.InList(cur
)
204 && strcmp(cur
,"erlang:")){
205 style
= SCE_ERLANG_BIFS
;
206 } else if (sc
.ch
== '(' || '/' == sc
.ch
){
207 style
= SCE_ERLANG_FUNCTION_NAME
;
209 style
= SCE_ERLANG_ATOM
;
212 sc
.ChangeState(style
);
213 sc
.SetState(SCE_ERLANG_DEFAULT
);
214 parse_state
= STATE_NULL
;
221 parse_state
= NODE_NAME_QUOTED
;
222 } else if ('\'' == sc
.ch
&& '\\' != sc
.chPrev
) {
223 sc
.ChangeState(SCE_ERLANG_ATOM
);
224 sc
.ForwardSetState(SCE_ERLANG_DEFAULT
);
225 parse_state
= STATE_NULL
;
229 /* -------------------------------------------------------------- */
230 /* Node names ----------------------------------------------------*/
231 case NODE_NAME_UNQUOTED
: {
233 sc
.SetState(SCE_ERLANG_DEFAULT
);
234 parse_state
= STATE_NULL
;
235 } else if (!IsAWordChar(sc
.ch
)) {
236 sc
.ChangeState(SCE_ERLANG_NODE_NAME
);
237 sc
.SetState(SCE_ERLANG_DEFAULT
);
238 parse_state
= STATE_NULL
;
242 case NODE_NAME_QUOTED
: {
244 sc
.SetState(SCE_ERLANG_DEFAULT
);
245 parse_state
= STATE_NULL
;
246 } else if ('\'' == sc
.ch
&& '\\' != sc
.chPrev
) {
247 sc
.ChangeState(SCE_ERLANG_NODE_NAME_QUOTED
);
248 sc
.ForwardSetState(SCE_ERLANG_DEFAULT
);
249 parse_state
= STATE_NULL
;
253 /* -------------------------------------------------------------- */
254 /* Records -------------------------------------------------------*/
255 case RECORD_START
: {
257 parse_state
= RECORD_QUOTED
;
258 } else if (isalpha(sc
.ch
) && islower(sc
.ch
)) {
259 parse_state
= RECORD_UNQUOTED
;
261 sc
.SetState(SCE_ERLANG_DEFAULT
);
262 parse_state
= STATE_NULL
;
266 case RECORD_UNQUOTED
: {
267 if (!IsAWordChar(sc
.ch
)) {
268 sc
.ChangeState(SCE_ERLANG_RECORD
);
269 sc
.SetState(SCE_ERLANG_DEFAULT
);
270 parse_state
= STATE_NULL
;
274 case RECORD_QUOTED
: {
275 if ('\'' == sc
.ch
&& '\\' != sc
.chPrev
) {
276 sc
.ChangeState(SCE_ERLANG_RECORD_QUOTED
);
277 sc
.ForwardSetState(SCE_ERLANG_DEFAULT
);
278 parse_state
= STATE_NULL
;
282 /* -------------------------------------------------------------- */
283 /* Macros --------------------------------------------------------*/
286 parse_state
= MACRO_QUOTED
;
287 } else if (isalpha(sc
.ch
)) {
288 parse_state
= MACRO_UNQUOTED
;
290 sc
.SetState(SCE_ERLANG_DEFAULT
);
291 parse_state
= STATE_NULL
;
295 case MACRO_UNQUOTED
: {
296 if (!IsAWordChar(sc
.ch
)) {
297 sc
.ChangeState(SCE_ERLANG_MACRO
);
298 sc
.SetState(SCE_ERLANG_DEFAULT
);
299 parse_state
= STATE_NULL
;
303 case MACRO_QUOTED
: {
304 if ('\'' == sc
.ch
&& '\\' != sc
.chPrev
) {
305 sc
.ChangeState(SCE_ERLANG_MACRO_QUOTED
);
306 sc
.ForwardSetState(SCE_ERLANG_DEFAULT
);
307 parse_state
= STATE_NULL
;
311 /* -------------------------------------------------------------- */
312 /* Numerics ------------------------------------------------------*/
314 case NUMERAL_START
: {
315 if (isdigit(sc
.ch
)) {
317 radix_digits
+= sc
.ch
- '0'; // Assuming ASCII here!
318 } else if ('#' == sc
.ch
) {
319 if (2 > radix_digits
|| 36 < radix_digits
) {
320 sc
.SetState(SCE_ERLANG_DEFAULT
);
321 parse_state
= STATE_NULL
;
323 parse_state
= NUMERAL_BASE_VALUE
;
325 } else if ('.' == sc
.ch
&& isdigit(sc
.chNext
)) {
327 parse_state
= NUMERAL_FLOAT
;
328 } else if ('e' == sc
.ch
|| 'E' == sc
.ch
) {
330 parse_state
= NUMERAL_EXPONENT
;
333 sc
.ChangeState(SCE_ERLANG_NUMBER
);
334 sc
.SetState(SCE_ERLANG_DEFAULT
);
335 parse_state
= STATE_NULL
;
339 /* Integer in other base than 10 (x#yyy) */
340 case NUMERAL_BASE_VALUE
: {
341 if (!is_radix(radix_digits
,sc
.ch
)) {
345 sc
.ChangeState(SCE_ERLANG_NUMBER
);
347 sc
.SetState(SCE_ERLANG_DEFAULT
);
348 parse_state
= STATE_NULL
;
353 case NUMERAL_FLOAT
: {
354 if ('e' == sc
.ch
|| 'E' == sc
.ch
) {
356 parse_state
= NUMERAL_EXPONENT
;
357 } else if (!isdigit(sc
.ch
)) {
358 sc
.ChangeState(SCE_ERLANG_NUMBER
);
359 sc
.SetState(SCE_ERLANG_DEFAULT
);
360 parse_state
= STATE_NULL
;
364 /* Exponent, either integer or float (xEyy, x.yyEzzz) */
365 case NUMERAL_EXPONENT
: {
366 if (('-' == sc
.ch
|| '+' == sc
.ch
)
367 && (isdigit(sc
.chNext
))) {
369 } else if (!isdigit(sc
.ch
)) {
370 if (0 < exponent_digits
)
371 sc
.ChangeState(SCE_ERLANG_NUMBER
);
372 sc
.SetState(SCE_ERLANG_DEFAULT
);
373 parse_state
= STATE_NULL
;
379 /* -------------------------------------------------------------- */
380 /* Preprocessor --------------------------------------------------*/
381 case PREPROCESSOR
: {
382 if (!IsAWordChar(sc
.ch
)) {
384 sc
.GetCurrent(cur
, sizeof(cur
));
385 if (erlangPreproc
.InList(cur
)) {
386 style
= SCE_ERLANG_PREPROC
;
387 } else if (erlangModulesAtt
.InList(cur
)) {
388 style
= SCE_ERLANG_MODULES_ATT
;
391 sc
.ChangeState(style
);
392 sc
.SetState(SCE_ERLANG_DEFAULT
);
393 parse_state
= STATE_NULL
;
399 } /* End of : STATE_NULL != parse_state */
403 case SCE_ERLANG_VARIABLE
: {
404 if (!IsAWordChar(sc
.ch
))
405 sc
.SetState(SCE_ERLANG_DEFAULT
);
407 case SCE_ERLANG_STRING
: {
408 if (sc
.ch
== '\"' && sc
.chPrev
!= '\\')
409 sc
.ForwardSetState(SCE_ERLANG_DEFAULT
);
411 case SCE_ERLANG_COMMENT
: {
413 sc
.SetState(SCE_ERLANG_DEFAULT
);
415 case SCE_ERLANG_CHARACTER
: {
416 if (sc
.chPrev
== '\\') {
417 sc
.ForwardSetState(SCE_ERLANG_DEFAULT
);
418 } else if (sc
.ch
!= '\\') {
419 sc
.ForwardSetState(SCE_ERLANG_DEFAULT
);
422 case SCE_ERLANG_OPERATOR
: {
423 if (sc
.chPrev
== '.') {
424 if (sc
.ch
== '*' || sc
.ch
== '/' || sc
.ch
== '\\'
426 sc
.ForwardSetState(SCE_ERLANG_DEFAULT
);
427 } else if (sc
.ch
== '\'') {
428 sc
.ForwardSetState(SCE_ERLANG_DEFAULT
);
430 sc
.SetState(SCE_ERLANG_DEFAULT
);
433 sc
.SetState(SCE_ERLANG_DEFAULT
);
439 if (sc
.state
== SCE_ERLANG_DEFAULT
) {
440 bool no_new_state
= false;
443 case '\"' : sc
.SetState(SCE_ERLANG_STRING
); break;
444 case '$' : sc
.SetState(SCE_ERLANG_CHARACTER
); break;
446 parse_state
= COMMENT
;
447 sc
.SetState(SCE_ERLANG_COMMENT
);
450 parse_state
= RECORD_START
;
451 sc
.SetState(SCE_ERLANG_UNKNOWN
);
454 parse_state
= MACRO_START
;
455 sc
.SetState(SCE_ERLANG_UNKNOWN
);
458 parse_state
= ATOM_QUOTED
;
459 sc
.SetState(SCE_ERLANG_UNKNOWN
);
463 if (IsADigit(sc
.chNext
)) {
464 parse_state
= NUMERAL_START
;
466 sc
.SetState(SCE_ERLANG_UNKNOWN
);
467 } else if (sc
.ch
!= '+') {
468 parse_state
= PREPROCESSOR
;
469 sc
.SetState(SCE_ERLANG_UNKNOWN
);
472 default : no_new_state
= true;
476 if (isdigit(sc
.ch
)) {
477 parse_state
= NUMERAL_START
;
478 radix_digits
= sc
.ch
- '0';
479 sc
.SetState(SCE_ERLANG_UNKNOWN
);
480 } else if (isupper(sc
.ch
) || '_' == sc
.ch
) {
481 sc
.SetState(SCE_ERLANG_VARIABLE
);
482 } else if (isalpha(sc
.ch
)) {
483 parse_state
= ATOM_UNQUOTED
;
484 sc
.SetState(SCE_ERLANG_UNKNOWN
);
485 } else if (isoperator(static_cast<char>(sc
.ch
))
487 sc
.SetState(SCE_ERLANG_OPERATOR
);
496 static int ClassifyErlangFoldPoint(
502 if (styler
.Match(keyword_start
,"case")
504 styler
.Match(keyword_start
,"fun")
505 && (SCE_ERLANG_FUNCTION_NAME
!= styleNext
)
507 || styler
.Match(keyword_start
,"if")
508 || styler
.Match(keyword_start
,"query")
509 || styler
.Match(keyword_start
,"receive")
512 } else if (styler
.Match(keyword_start
,"end")) {
519 static void FoldErlangDoc(
520 unsigned int startPos
, int length
, int initStyle
,
521 WordList
** /*keywordlists*/, Accessor
&styler
523 unsigned int endPos
= startPos
+ length
;
524 int currentLine
= styler
.GetLine(startPos
);
526 int previousLevel
= styler
.LevelAt(currentLine
) & SC_FOLDLEVELNUMBERMASK
;
527 int currentLevel
= previousLevel
;
528 int styleNext
= styler
.StyleAt(startPos
);
529 int style
= initStyle
;
531 int keyword_start
= 0;
533 char chNext
= styler
.SafeGetCharAt(startPos
);
536 for (unsigned int i
= startPos
; i
< endPos
; i
++) {
538 chNext
= styler
.SafeGetCharAt(i
+ 1);
543 styleNext
= styler
.StyleAt(i
+ 1);
544 atEOL
= ((ch
== '\r') && (chNext
!= '\n')) || (ch
== '\n');
546 if (stylePrev
!= SCE_ERLANG_KEYWORD
547 && style
== SCE_ERLANG_KEYWORD
) {
552 if (stylePrev
== SCE_ERLANG_KEYWORD
553 && style
!= SCE_ERLANG_KEYWORD
554 && style
!= SCE_ERLANG_ATOM
556 currentLevel
+= ClassifyErlangFoldPoint(styler
,
562 if (style
== SCE_ERLANG_COMMENT
563 || style
== SCE_ERLANG_COMMENT_MODULE
564 || style
== SCE_ERLANG_COMMENT_FUNCTION
) {
566 if (ch
== '%' && chNext
== '{') {
568 } else if (ch
== '%' && chNext
== '}') {
574 if (style
== SCE_ERLANG_OPERATOR
) {
575 if (ch
== '{' || ch
== '(' || ch
== '[') {
577 } else if (ch
== '}' || ch
== ')' || ch
== ']') {
586 if (currentLevel
> previousLevel
)
587 lev
|= SC_FOLDLEVELHEADERFLAG
;
589 if (lev
!= styler
.LevelAt(currentLine
))
590 styler
.SetLevel(currentLine
, lev
);
593 previousLevel
= currentLevel
;
598 // Fill in the real level of the next line, keeping the current flags as they will be filled in later
599 styler
.SetLevel(currentLine
,
601 | (styler
.LevelAt(currentLine
) & ~SC_FOLDLEVELNUMBERMASK
));
604 static const char * const erlangWordListDesc
[] = {
605 "Erlang Reserved words",
607 "Erlang Preprocessor",
608 "Erlang Module Attributes",
609 "Erlang Documentation",
610 "Erlang Documentation Macro",
614 LexerModule
lmErlang(