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(