1 // Scintilla source code edit control
2 /** @file LexErlang.cxx
4 ** Written by Peter-Henry Mander, based on Matlab lexer by Jose' Fonseca
6 // Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
7 // The License.txt file describes the conditions under which this software may be distributed.
19 #include "StyleContext.h"
21 #include "Scintilla.h"
26 o _Param should be a new lexical type
29 static int is_radix(int radix
, int ch
) {
31 if ( 16 < radix
|| 2 > radix
) {
36 } else if ( isxdigit(ch
) ) {
37 digit
= toupper(ch
) - 'A' + 10;
41 if ( digit
< radix
) {
63 NUMERAL_RADIX_LITERAL
,
64 NUMERAL_SPECULATIVE_MANTISSA
,
65 NUMERAL_FLOAT_MANTISSA
,
66 NUMERAL_FLOAT_EXPONENT
,
67 NUMERAL_FLOAT_SIGNED_EXPONENT
,
71 static void ColouriseErlangDoc(unsigned int startPos
, int length
, int initStyle
,
72 WordList
*keywordlists
[], Accessor
&styler
) {
74 WordList
&keywords
= *keywordlists
[0];
76 styler
.StartAt(startPos
);
78 StyleContext
sc(startPos
, length
, initStyle
, styler
);
79 atom_parse_state_t parse_state
= STATE_NULL
;
81 int exponent_digits
= 0;
82 for (; sc
.More(); sc
.Forward()) {
83 if ( STATE_NULL
!= parse_state
) {
84 switch (parse_state
) {
86 sc
.SetState(SCE_ERLANG_DEFAULT
);
90 parse_state
= NODE_NAME_UNQUOTED
;
91 } else if ( !isalnum(sc
.ch
) && sc
.ch
!= '_' ) {
93 sc
.GetCurrent(s
, sizeof(s
));
94 if (keywords
.InList(s
)) {
95 sc
.ChangeState(SCE_ERLANG_KEYWORD
);
96 sc
.SetState(SCE_ERLANG_DEFAULT
);
97 parse_state
= STATE_NULL
;
100 parse_state
= ATOM_FUN_NAME
;
102 sc
.ChangeState(SCE_ERLANG_ATOM
);
103 sc
.SetState(SCE_ERLANG_DEFAULT
);
104 parse_state
= STATE_NULL
;
111 parse_state
= NODE_NAME_QUOTED
;
112 } else if ( '\'' == sc
.ch
&& '\\' != sc
.chPrev
) {
113 sc
.ChangeState(SCE_ERLANG_ATOM
);
114 sc
.ForwardSetState(SCE_ERLANG_DEFAULT
);
115 parse_state
= STATE_NULL
;
119 if ( !isdigit(sc
.ch
) ) {
120 sc
.ChangeState(SCE_ERLANG_FUNCTION_NAME
);
121 sc
.SetState(SCE_ERLANG_DEFAULT
);
122 parse_state
= STATE_NULL
;
125 case NODE_NAME_QUOTED
:
126 if ( '@' == sc
.ch
) {
127 sc
.SetState(SCE_ERLANG_DEFAULT
);
128 parse_state
= STATE_NULL
;
129 } else if ( '\'' == sc
.ch
&& '\\' != sc
.chPrev
) {
130 sc
.ChangeState(SCE_ERLANG_NODE_NAME
);
131 sc
.ForwardSetState(SCE_ERLANG_DEFAULT
);
132 parse_state
= STATE_NULL
;
135 case NODE_NAME_UNQUOTED
:
136 if ( '@' == sc
.ch
) {
137 sc
.SetState(SCE_ERLANG_DEFAULT
);
138 parse_state
= STATE_NULL
;
139 } else if ( !isalnum(sc
.ch
) && sc
.ch
!= '_' ) {
140 sc
.ChangeState(SCE_ERLANG_NODE_NAME
);
141 sc
.SetState(SCE_ERLANG_DEFAULT
);
142 parse_state
= STATE_NULL
;
146 if ( '\'' == sc
.ch
) {
147 parse_state
= RECORD_QUOTED
;
148 } else if (isalpha(sc
.ch
) && islower(sc
.ch
)) {
149 parse_state
= RECORD_UNQUOTED
;
151 sc
.SetState(SCE_ERLANG_DEFAULT
);
152 parse_state
= STATE_NULL
;
156 if ( '\'' == sc
.ch
&& '\\' != sc
.chPrev
) {
157 sc
.ChangeState(SCE_ERLANG_RECORD
);
158 sc
.ForwardSetState(SCE_ERLANG_DEFAULT
);
159 parse_state
= STATE_NULL
;
162 case RECORD_UNQUOTED
:
163 if ( !isalpha(sc
.ch
) && '_' != sc
.ch
) {
164 sc
.ChangeState(SCE_ERLANG_RECORD
);
165 sc
.SetState(SCE_ERLANG_DEFAULT
);
166 parse_state
= STATE_NULL
;
170 if ( '\'' == sc
.ch
) {
171 parse_state
= MACRO_QUOTED
;
172 } else if (isalpha(sc
.ch
)) {
173 parse_state
= MACRO_UNQUOTED
;
175 sc
.SetState(SCE_ERLANG_DEFAULT
);
176 parse_state
= STATE_NULL
;
180 if ( !isalpha(sc
.ch
) && '_' != sc
.ch
) {
181 sc
.ChangeState(SCE_ERLANG_MACRO
);
182 sc
.SetState(SCE_ERLANG_DEFAULT
);
183 parse_state
= STATE_NULL
;
187 if ( '\'' == sc
.ch
&& '\\' != sc
.chPrev
) {
188 sc
.ChangeState(SCE_ERLANG_MACRO
);
189 sc
.ForwardSetState(SCE_ERLANG_DEFAULT
);
190 parse_state
= STATE_NULL
;
194 if ( isdigit(sc
.ch
) ) {
196 radix_digits
+= sc
.ch
- '0'; // Assuming ASCII here!
197 } else if ( '#' == sc
.ch
) {
198 if ( 2 > radix_digits
|| 16 < radix_digits
) {
199 sc
.SetState(SCE_ERLANG_DEFAULT
);
200 parse_state
= STATE_NULL
;
202 parse_state
= NUMERAL_RADIX_LITERAL
;
204 } else if ( '.' == sc
.ch
&& isdigit(sc
.chNext
)) {
206 parse_state
= NUMERAL_FLOAT_MANTISSA
;
207 } else if ( 'e' == sc
.ch
|| 'E' == sc
.ch
) {
209 parse_state
= NUMERAL_FLOAT_EXPONENT
;
212 sc
.ChangeState(SCE_ERLANG_NUMBER
);
213 sc
.SetState(SCE_ERLANG_DEFAULT
);
214 parse_state
= STATE_NULL
;
217 case NUMERAL_RADIX_LITERAL
:
218 if ( !is_radix(radix_digits
,sc
.ch
) ) {
220 if ( !isalnum(sc
.ch
) ) {
221 sc
.ChangeState(SCE_ERLANG_NUMBER
);
223 sc
.SetState(SCE_ERLANG_DEFAULT
);
224 parse_state
= STATE_NULL
;
227 case NUMERAL_FLOAT_MANTISSA
:
228 if ( 'e' == sc
.ch
|| 'E' == sc
.ch
) {
230 parse_state
= NUMERAL_FLOAT_EXPONENT
;
231 } else if ( !isdigit(sc
.ch
) ) {
232 sc
.ChangeState(SCE_ERLANG_NUMBER
);
233 sc
.SetState(SCE_ERLANG_DEFAULT
);
234 parse_state
= STATE_NULL
;
237 case NUMERAL_FLOAT_EXPONENT
:
238 if ( '-' == sc
.ch
|| '+' == sc
.ch
) {
239 parse_state
= NUMERAL_FLOAT_SIGNED_EXPONENT
;
240 } else if ( !isdigit(sc
.ch
) ) {
241 if ( 0 < exponent_digits
) {
242 sc
.ChangeState(SCE_ERLANG_NUMBER
);
244 sc
.SetState(SCE_ERLANG_DEFAULT
);
245 parse_state
= STATE_NULL
;
250 case NUMERAL_FLOAT_SIGNED_EXPONENT
:
251 if ( !isdigit(sc
.ch
) ) {
252 if ( 0 < exponent_digits
) {
253 sc
.ChangeState(SCE_ERLANG_NUMBER
);
255 sc
.SetState(SCE_ERLANG_DEFAULT
);
256 parse_state
= STATE_NULL
;
262 if ( !isdigit(sc
.ch
) ) {
263 sc
.ChangeState(SCE_ERLANG_NUMBER
);
264 sc
.SetState(SCE_ERLANG_DEFAULT
);
265 parse_state
= STATE_NULL
;
266 } else if ( '.' == sc
.ch
) {
267 parse_state
= NUMERAL_FLOAT_MANTISSA
;
270 case NUMERAL_SPECULATIVE_MANTISSA
:
271 if ( !isdigit(sc
.ch
) ) {
272 sc
.ChangeState(SCE_ERLANG_OPERATOR
);
273 sc
.SetState(SCE_ERLANG_DEFAULT
);
274 parse_state
= STATE_NULL
;
276 parse_state
= NUMERAL_FLOAT_MANTISSA
;
280 sc
.SetState(SCE_ERLANG_DEFAULT
);
281 parse_state
= STATE_NULL
;
284 } else if (sc
.state
== SCE_ERLANG_OPERATOR
) {
285 if (sc
.chPrev
== '.') {
286 if (sc
.ch
== '*' || sc
.ch
== '/' || sc
.ch
== '\\' || sc
.ch
== '^') {
287 sc
.ForwardSetState(SCE_ERLANG_DEFAULT
);
288 } else if (sc
.ch
== '\'') {
289 sc
.ForwardSetState(SCE_ERLANG_DEFAULT
);
291 sc
.SetState(SCE_ERLANG_DEFAULT
);
294 sc
.SetState(SCE_ERLANG_DEFAULT
);
296 } else if (sc
.state
== SCE_ERLANG_VARIABLE
) {
297 if (!isalnum(sc
.ch
) && sc
.ch
!= '_') {
298 sc
.SetState(SCE_ERLANG_DEFAULT
);
300 } else if (sc
.state
== SCE_ERLANG_STRING
) {
301 if (sc
.ch
== '\"' && sc
.chPrev
!= '\\') {
302 sc
.ForwardSetState(SCE_ERLANG_DEFAULT
);
304 } else if (sc
.state
== SCE_ERLANG_COMMENT
) {
306 sc
.SetState(SCE_ERLANG_DEFAULT
);
308 } else if (sc
.state
== SCE_ERLANG_CHARACTER
) {
309 if ( sc
.chPrev
== '\\' ) {
310 sc
.ForwardSetState(SCE_ERLANG_DEFAULT
);
311 } else if ( sc
.ch
!= '\\' ) {
312 sc
.ForwardSetState(SCE_ERLANG_DEFAULT
);
316 if (sc
.state
== SCE_ERLANG_DEFAULT
) {
318 sc
.SetState(SCE_ERLANG_COMMENT
);
319 } else if (sc
.ch
== '\"') {
320 sc
.SetState(SCE_ERLANG_STRING
);
321 } else if (sc
.ch
== '#') {
322 parse_state
= RECORD_START
;
323 sc
.SetState(SCE_ERLANG_UNKNOWN
);
324 } else if (sc
.ch
== '?') {
325 parse_state
= MACRO_START
;
326 sc
.SetState(SCE_ERLANG_UNKNOWN
);
327 } else if (sc
.ch
== '$') {
328 sc
.SetState(SCE_ERLANG_CHARACTER
);
329 } else if (sc
.ch
== '\'') {
330 parse_state
= ATOM_QUOTED
;
331 sc
.SetState(SCE_ERLANG_UNKNOWN
);
332 } else if ( isdigit(sc
.ch
) ) {
333 parse_state
= NUMERAL_START
;
334 radix_digits
= sc
.ch
- '0';
335 sc
.SetState(SCE_ERLANG_UNKNOWN
);
336 } else if ( '.' == sc
.ch
) {
337 parse_state
= NUMERAL_SPECULATIVE_MANTISSA
;
338 sc
.SetState(SCE_ERLANG_UNKNOWN
);
339 } else if (isalpha(sc
.ch
) && isupper(sc
.ch
)) {
340 sc
.SetState(SCE_ERLANG_VARIABLE
);
341 } else if (isalpha(sc
.ch
)) {
342 parse_state
= ATOM_UNQUOTED
;
343 sc
.SetState(SCE_ERLANG_UNKNOWN
);
344 } else if (isoperator(static_cast<char>(sc
.ch
)) || sc
.ch
== '\\') {
345 sc
.SetState(SCE_ERLANG_OPERATOR
);
352 static int ClassifyFoldPointErlang(
358 if ( styler
.Match(keyword_start
,"case")
360 styler
.Match(keyword_start
,"fun")
361 && SCE_ERLANG_FUNCTION_NAME
!= styleNext
)
362 || styler
.Match(keyword_start
,"if")
363 || styler
.Match(keyword_start
,"query")
364 || styler
.Match(keyword_start
,"receive")
367 } else if ( styler
.Match(keyword_start
,"end") ) {
374 static void FoldErlangDoc(
375 unsigned int startPos
, int length
, int initStyle
,
376 WordList
** /*keywordlists*/, Accessor
&styler
378 unsigned int endPos
= startPos
+ length
;
379 //~ int visibleChars = 0;
380 int lineCurrent
= styler
.GetLine(startPos
);
381 int levelPrev
= styler
.LevelAt(lineCurrent
) & SC_FOLDLEVELNUMBERMASK
;
382 int levelCurrent
= levelPrev
;
383 char chNext
= styler
.SafeGetCharAt(startPos
);
384 int styleNext
= styler
.StyleAt(startPos
);
385 int style
= initStyle
;
386 int keyword_start
= 0;
388 bool fold_keywords
= true;
389 bool fold_comments
= true;
390 bool fold_braces
= true;
391 bool fold_function_clauses
= false;
392 bool fold_clauses
= false;
394 //int clause_level = 0;
396 for (unsigned int i
= startPos
; i
< endPos
; i
++) {
398 chNext
= styler
.SafeGetCharAt(i
+ 1);
399 int stylePrev
= style
;
401 styleNext
= styler
.StyleAt(i
+ 1);
402 bool atEOL
= (ch
== '\r' && chNext
!= '\n') || (ch
== '\n');
404 if ( (stylePrev
!= SCE_ERLANG_KEYWORD
) && (style
== SCE_ERLANG_KEYWORD
) ) {
407 if ( fold_keywords
) {
408 if ( (stylePrev
== SCE_ERLANG_KEYWORD
)
409 && (style
!= SCE_ERLANG_KEYWORD
)
410 && (style
!= SCE_ERLANG_ATOM
)
412 levelCurrent
+= ClassifyFoldPointErlang(styler
,styleNext
,keyword_start
);
416 if ( fold_comments
) {
417 if (style
== SCE_ERLANG_COMMENT
) {
418 if ((ch
== '%') && (chNext
== '{')) {
420 } else if ((ch
== '%') && (chNext
== '}')) {
426 if ( fold_function_clauses
) {
427 if ( (SC_FOLDLEVELBASE
== levelCurrent
) /*&& (style == SCE_ERLANG_OPERATOR)*/ ) {
428 if ( (ch
== '-') && (chNext
== '>')) {
429 //~ fprintf(stderr,"levelCurrent=%d\n", levelCurrent);
431 //~ if ( 0 < clause_level )
435 //~ if ( (stylePrev != SCE_ERLANG_RECORD)
436 //~ && (style != SCE_ERLANG_NUMBER)
437 //~ && (style != SCE_ERLANG_STRING)
438 //~ && (style != SCE_ERLANG_COMMENT)
440 if ( (SC_FOLDLEVELBASE
+1 == levelCurrent
) && (ch
== '.') ) {
442 //~ if ( 0 == clause_level )
448 if ( fold_clauses
) {
449 if ( (0 < levelCurrent
) && (style
== SCE_ERLANG_OPERATOR
) ) {
450 if ((ch
== '-') && (chNext
== '>')) {
457 if ( (stylePrev
!= SCE_ERLANG_RECORD
)
458 && (style
!= SCE_ERLANG_NUMBER
)
459 && (style
!= SCE_ERLANG_STRING
)
460 && (style
!= SCE_ERLANG_COMMENT
)
466 if ( (stylePrev
== SCE_ERLANG_KEYWORD
)
467 && (style
!= SCE_ERLANG_KEYWORD
)
468 && (style
!= SCE_ERLANG_ATOM
)
470 styler
.Match(keyword_start
,"end") // 'end' counted twice if fold_keywords too
471 || styler
.Match(keyword_start
,"after") )
478 if (style
== SCE_ERLANG_OPERATOR
) {
479 if ( (ch
== '{') || (ch
== '(') || (ch
== '[') ) {
481 } else if ( (ch
== '}') || (ch
== ')') || (ch
== ']') ) {
489 //~ if (visibleChars == 0 && foldCompact)
490 //~ lev |= SC_FOLDLEVELWHITEFLAG;
491 //~ if ((levelCurrent > levelPrev) && (visibleChars > 0))
492 if ((levelCurrent
> levelPrev
)) {
493 lev
|= SC_FOLDLEVELHEADERFLAG
;
495 if (lev
!= styler
.LevelAt(lineCurrent
)) {
496 styler
.SetLevel(lineCurrent
, lev
);
499 levelPrev
= levelCurrent
;
500 //~ visibleChars = 0;
502 //~ if (!isspacechar(ch))
506 // Fill in the real level of the next line, keeping the current flags as they will be filled in later
507 int flagsNext
= styler
.LevelAt(lineCurrent
) & ~SC_FOLDLEVELNUMBERMASK
;
508 styler
.SetLevel(lineCurrent
, levelPrev
| flagsNext
);
511 static const char * const erlangWordListDesc
[] = {
516 LexerModule
lmErlang(