You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tqscintilla/src/LexErlang.cpp

523 lines
16 KiB

// Scintilla source code edit control
/** @file LexErlang.cxx
** Lexer for Erlang.
** Written by Peter-Henry Mander, based on Matlab lexer by José Fonseca
**/
// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>
#include "Platform.h"
#include "PropSet.h"
#include "Accessor.h"
#include "StyleContext.h"
#include "KeyWords.h"
#include "Scintilla.h"
#include "SciLexer.h"
/*
TODO:
o _Param should be a new lexical type
*/
static int is_radix(int radix, int ch) {
int digit;
if ( 16 < radix || 2 > radix ) {
return 0;
}
if ( isdigit(ch) ) {
digit = ch - '0';
} else if ( isxdigit(ch) ) {
digit = toupper(ch) - 'A' + 10;
} else {
return 0;
}
if ( digit < radix ) {
return 1;
} else {
return 0;
}
}
typedef enum {
STATE_NULL,
ATOM_UNQUOTED,
ATOM_QUOTED,
ATOM_FUN_NAME,
NODE_NAME_UNQUOTED,
NODE_NAME_QUOTED,
MACRO_START,
MACRO_UNQUOTED,
MACRO_QUOTED,
RECORD_START,
RECORD_UNQUOTED,
RECORD_QUOTED,
NUMERAL_START,
NUMERAL_SIGNED,
NUMERAL_RADIX_LITERAL,
NUMERAL_SPECULATIVE_MANTISSA,
NUMERAL_FLOAT_MANTISSA,
NUMERAL_FLOAT_EXPONENT,
NUMERAL_FLOAT_SIGNED_EXPONENT,
PARSE_ERROR
} atom_parse_state_t;
static void ColouriseErlangDoc(unsigned int startPos, int length, int initStyle,
WordList *keywordlists[], Accessor &styler) {
WordList &keywords = *keywordlists[0];
styler.StartAt(startPos);
StyleContext sc(startPos, length, initStyle, styler);
atom_parse_state_t parse_state = STATE_NULL;
int radix_digits = 0;
int exponent_digits = 0;
for (; sc.More(); sc.Forward()) {
if ( STATE_NULL != parse_state ) {
switch (parse_state) {
case STATE_NULL:
sc.SetState(SCE_ERLANG_DEFAULT);
break;
case ATOM_UNQUOTED:
if ( '@' == sc.ch ){
parse_state = NODE_NAME_UNQUOTED;
} else if ( !isalnum(sc.ch) && sc.ch != '_' ) {
char s[100];
sc.GetCurrent(s, sizeof(s));
if (keywords.InList(s)) {
sc.ChangeState(SCE_ERLANG_KEYWORD);
sc.SetState(SCE_ERLANG_DEFAULT);
parse_state = STATE_NULL;
} else {
if ( '/' == sc.ch ) {
parse_state = ATOM_FUN_NAME;
} else {
sc.ChangeState(SCE_ERLANG_ATOM);
sc.SetState(SCE_ERLANG_DEFAULT);
parse_state = STATE_NULL;
}
}
}
break;
case ATOM_QUOTED:
if ( '@' == sc.ch ){
parse_state = NODE_NAME_QUOTED;
} else if ( '\'' == sc.ch && '\\' != sc.chPrev ) {
sc.ChangeState(SCE_ERLANG_ATOM);
sc.ForwardSetState(SCE_ERLANG_DEFAULT);
parse_state = STATE_NULL;
}
break;
case ATOM_FUN_NAME:
if ( !isdigit(sc.ch) ) {
sc.ChangeState(SCE_ERLANG_FUNCTION_NAME);
sc.SetState(SCE_ERLANG_DEFAULT);
parse_state = STATE_NULL;
}
break;
case NODE_NAME_QUOTED:
if ( '@' == sc.ch ) {
sc.SetState(SCE_ERLANG_DEFAULT);
parse_state = STATE_NULL;
} else if ( '\'' == sc.ch && '\\' != sc.chPrev ) {
sc.ChangeState(SCE_ERLANG_NODE_NAME);
sc.ForwardSetState(SCE_ERLANG_DEFAULT);
parse_state = STATE_NULL;
}
break;
case NODE_NAME_UNQUOTED:
if ( '@' == sc.ch ) {
sc.SetState(SCE_ERLANG_DEFAULT);
parse_state = STATE_NULL;
} else if ( !isalnum(sc.ch) && sc.ch != '_' ) {
sc.ChangeState(SCE_ERLANG_NODE_NAME);
sc.SetState(SCE_ERLANG_DEFAULT);
parse_state = STATE_NULL;
}
break;
case RECORD_START:
if ( '\'' == sc.ch ) {
parse_state = RECORD_QUOTED;
} else if (isalpha(sc.ch) && islower(sc.ch)) {
parse_state = RECORD_UNQUOTED;
} else { // error
sc.SetState(SCE_ERLANG_DEFAULT);
parse_state = STATE_NULL;
}
break;
case RECORD_QUOTED:
if ( '\'' == sc.ch && '\\' != sc.chPrev ) {
sc.ChangeState(SCE_ERLANG_RECORD);
sc.ForwardSetState(SCE_ERLANG_DEFAULT);
parse_state = STATE_NULL;
}
break;
case RECORD_UNQUOTED:
if ( !isalpha(sc.ch) && '_' != sc.ch ) {
sc.ChangeState(SCE_ERLANG_RECORD);
sc.SetState(SCE_ERLANG_DEFAULT);
parse_state = STATE_NULL;
}
break;
case MACRO_START:
if ( '\'' == sc.ch ) {
parse_state = MACRO_QUOTED;
} else if (isalpha(sc.ch)) {
parse_state = MACRO_UNQUOTED;
} else { // error
sc.SetState(SCE_ERLANG_DEFAULT);
parse_state = STATE_NULL;
}
break;
case MACRO_UNQUOTED:
if ( !isalpha(sc.ch) && '_' != sc.ch ) {
sc.ChangeState(SCE_ERLANG_MACRO);
sc.SetState(SCE_ERLANG_DEFAULT);
parse_state = STATE_NULL;
}
break;
case MACRO_QUOTED:
if ( '\'' == sc.ch && '\\' != sc.chPrev ) {
sc.ChangeState(SCE_ERLANG_MACRO);
sc.ForwardSetState(SCE_ERLANG_DEFAULT);
parse_state = STATE_NULL;
}
break;
case NUMERAL_START:
if ( isdigit(sc.ch) ) {
radix_digits *= 10;
radix_digits += sc.ch - '0'; // Assuming ASCII here!
} else if ( '#' == sc.ch ) {
if ( 2 > radix_digits || 16 < radix_digits) {
sc.SetState(SCE_ERLANG_DEFAULT);
parse_state = STATE_NULL;
} else {
parse_state = NUMERAL_RADIX_LITERAL;
}
} else if ( '.' == sc.ch && isdigit(sc.chNext)) {
radix_digits = 0;
parse_state = NUMERAL_FLOAT_MANTISSA;
} else if ( 'e' == sc.ch || 'E' == sc.ch ) {
exponent_digits = 0;
parse_state = NUMERAL_FLOAT_EXPONENT;
} else {
radix_digits = 0;
sc.ChangeState(SCE_ERLANG_NUMBER);
sc.SetState(SCE_ERLANG_DEFAULT);
parse_state = STATE_NULL;
}
break;
case NUMERAL_RADIX_LITERAL:
if ( !is_radix(radix_digits,sc.ch) ) {
radix_digits = 0;
if ( !isalnum(sc.ch) ) {
sc.ChangeState(SCE_ERLANG_NUMBER);
}
sc.SetState(SCE_ERLANG_DEFAULT);
parse_state = STATE_NULL;
}
break;
case NUMERAL_FLOAT_MANTISSA:
if ( 'e' == sc.ch || 'E' == sc.ch ) {
exponent_digits = 0;
parse_state = NUMERAL_FLOAT_EXPONENT;
} else if ( !isdigit(sc.ch) ) {
sc.ChangeState(SCE_ERLANG_NUMBER);
sc.SetState(SCE_ERLANG_DEFAULT);
parse_state = STATE_NULL;
}
break;
case NUMERAL_FLOAT_EXPONENT:
if ( '-' == sc.ch || '+' == sc.ch ) {
parse_state = NUMERAL_FLOAT_SIGNED_EXPONENT;
} else if ( !isdigit(sc.ch) ) {
if ( 0 < exponent_digits ) {
sc.ChangeState(SCE_ERLANG_NUMBER);
}
sc.SetState(SCE_ERLANG_DEFAULT);
parse_state = STATE_NULL;
} else {
++exponent_digits;
}
break;
case NUMERAL_FLOAT_SIGNED_EXPONENT:
if ( !isdigit(sc.ch) ) {
if ( 0 < exponent_digits ) {
sc.ChangeState(SCE_ERLANG_NUMBER);
}
sc.SetState(SCE_ERLANG_DEFAULT);
parse_state = STATE_NULL;
} else {
++exponent_digits;
}
break;
case NUMERAL_SIGNED:
if ( !isdigit(sc.ch) ) {
sc.ChangeState(SCE_ERLANG_NUMBER);
sc.SetState(SCE_ERLANG_DEFAULT);
parse_state = STATE_NULL;
} else if ( '.' == sc.ch ) {
parse_state = NUMERAL_FLOAT_MANTISSA;
}
break;
case NUMERAL_SPECULATIVE_MANTISSA:
if ( !isdigit(sc.ch) ) {
sc.ChangeState(SCE_ERLANG_OPERATOR);
sc.SetState(SCE_ERLANG_DEFAULT);
parse_state = STATE_NULL;
} else {
parse_state = NUMERAL_FLOAT_MANTISSA;
}
break;
case PARSE_ERROR:
sc.SetState(SCE_ERLANG_DEFAULT);
parse_state = STATE_NULL;
break;
}
} else if (sc.state == SCE_ERLANG_OPERATOR) {
if (sc.chPrev == '.') {
if (sc.ch == '*' || sc.ch == '/' || sc.ch == '\\' || sc.ch == '^') {
sc.ForwardSetState(SCE_ERLANG_DEFAULT);
} else if (sc.ch == '\'') {
sc.ForwardSetState(SCE_ERLANG_DEFAULT);
} else {
sc.SetState(SCE_ERLANG_DEFAULT);
}
} else {
sc.SetState(SCE_ERLANG_DEFAULT);
}
} else if (sc.state == SCE_ERLANG_VARIABLE) {
if (!isalnum(sc.ch) && sc.ch != '_') {
sc.SetState(SCE_ERLANG_DEFAULT);
}
} else if (sc.state == SCE_ERLANG_STRING) {
if (sc.ch == '\"' && sc.chPrev != '\\') {
sc.ForwardSetState(SCE_ERLANG_DEFAULT);
}
} else if (sc.state == SCE_ERLANG_COMMENT ) {
if (sc.atLineEnd) {
sc.SetState(SCE_ERLANG_DEFAULT);
}
} else if (sc.state == SCE_ERLANG_CHARACTER ) {
if ( sc.chPrev == '\\' ) {
sc.ForwardSetState(SCE_ERLANG_DEFAULT);
} else if ( sc.ch != '\\' ) {
sc.ForwardSetState(SCE_ERLANG_DEFAULT);
}
}
if (sc.state == SCE_ERLANG_DEFAULT) {
if (sc.ch == '%') {
sc.SetState(SCE_ERLANG_COMMENT);
} else if (sc.ch == '\"') {
sc.SetState(SCE_ERLANG_STRING);
} else if (sc.ch == '#') {
parse_state = RECORD_START;
sc.SetState(SCE_ERLANG_UNKNOWN);
} else if (sc.ch == '?') {
parse_state = MACRO_START;
sc.SetState(SCE_ERLANG_UNKNOWN);
} else if (sc.ch == '$') {
sc.SetState(SCE_ERLANG_CHARACTER);
} else if (sc.ch == '\'') {
parse_state = ATOM_QUOTED;
sc.SetState(SCE_ERLANG_UNKNOWN);
} else if ( isdigit(sc.ch) ) {
parse_state = NUMERAL_START;
radix_digits = sc.ch - '0';
sc.SetState(SCE_ERLANG_UNKNOWN);
} else if ( '.' == sc.ch ) {
parse_state = NUMERAL_SPECULATIVE_MANTISSA;
sc.SetState(SCE_ERLANG_UNKNOWN);
} else if (isalpha(sc.ch) && isupper(sc.ch)) {
sc.SetState(SCE_ERLANG_VARIABLE);
} else if (isalpha(sc.ch)) {
parse_state = ATOM_UNQUOTED;
sc.SetState(SCE_ERLANG_UNKNOWN);
} else if (isoperator(static_cast<char>(sc.ch)) || sc.ch == '\\') {
sc.SetState(SCE_ERLANG_OPERATOR);
}
}
}
sc.Complete();
}
static int ClassifyFoldPointErlang(
Accessor &styler,
int styleNext,
int keyword_start
) {
int lev = 0;
if ( styler.Match(keyword_start,"case")
|| (
styler.Match(keyword_start,"fun")
&& SCE_ERLANG_FUNCTION_NAME != styleNext)
|| styler.Match(keyword_start,"if")
|| styler.Match(keyword_start,"query")
|| styler.Match(keyword_start,"receive")
) {
++lev;
} else if ( styler.Match(keyword_start,"end") ) {
--lev;
}
return lev;
}
static void FoldErlangDoc(
unsigned int startPos, int length, int initStyle,
WordList** /*keywordlists*/, Accessor &styler
) {
unsigned int endPos = startPos + length;
//~ int visibleChars = 0;
int lineCurrent = styler.GetLine(startPos);
int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
int levelCurrent = levelPrev;
char chNext = styler.SafeGetCharAt(startPos);
int styleNext = styler.StyleAt(startPos);
int style = initStyle;
int keyword_start = 0;
bool fold_keywords = true;
bool fold_comments = true;
bool fold_braces = true;
bool fold_function_clauses = false;
bool fold_clauses = false;
//int clause_level = 0;
for (unsigned int i = startPos; i < endPos; i++) {
char ch = chNext;
chNext = styler.SafeGetCharAt(i + 1);
int stylePrev = style;
style = styleNext;
styleNext = styler.StyleAt(i + 1);
bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
if ( (stylePrev != SCE_ERLANG_KEYWORD) && (style == SCE_ERLANG_KEYWORD) ) {
keyword_start = i;
}
if ( fold_keywords ) {
if ( (stylePrev == SCE_ERLANG_KEYWORD)
&& (style != SCE_ERLANG_KEYWORD)
&& (style != SCE_ERLANG_ATOM)
) {
levelCurrent += ClassifyFoldPointErlang(styler,styleNext,keyword_start);
}
}
if ( fold_comments ) {
if (style == SCE_ERLANG_COMMENT) {
if ((ch == '%') && (chNext == '{')) {
levelCurrent++;
} else if ((ch == '%') && (chNext == '}')) {
levelCurrent--;
}
}
}
if ( fold_function_clauses ) {
if ( (SC_FOLDLEVELBASE == levelCurrent) /*&& (style == SCE_ERLANG_OPERATOR)*/ ) {
if ( (ch == '-') && (chNext == '>')) {
//~ fprintf(stderr,"levelCurrent=%d\n", levelCurrent);
//++clause_level;
//~ if ( 0 < clause_level )
++levelCurrent;
}
}
//~ if ( (stylePrev != SCE_ERLANG_RECORD)
//~ && (style != SCE_ERLANG_NUMBER)
//~ && (style != SCE_ERLANG_STRING)
//~ && (style != SCE_ERLANG_COMMENT)
//~ ) {
if ( (SC_FOLDLEVELBASE+1 == levelCurrent) && (ch == '.') ) {
//--clause_level;
//~ if ( 0 == clause_level )
--levelCurrent;
}
//~ }
}
if ( fold_clauses ) {
if ( (0 < levelCurrent) && (style == SCE_ERLANG_OPERATOR) ) {
if ((ch == '-') && (chNext == '>')) {
levelCurrent++;
}
if ( (ch == ';') ) {
levelCurrent--;
}
}
if ( (stylePrev != SCE_ERLANG_RECORD)
&& (style != SCE_ERLANG_NUMBER)
&& (style != SCE_ERLANG_STRING)
&& (style != SCE_ERLANG_COMMENT)
) {
if ( (ch == '.') ) {
levelCurrent--;
}
}
if ( (stylePrev == SCE_ERLANG_KEYWORD)
&& (style != SCE_ERLANG_KEYWORD)
&& (style != SCE_ERLANG_ATOM)
&& (
styler.Match(keyword_start,"end") // 'end' counted twice if fold_keywords too
|| styler.Match(keyword_start,"after") )
) {
levelCurrent--;
}
}
if ( fold_braces ) {
if (style == SCE_ERLANG_OPERATOR) {
if ( (ch == '{') || (ch == '(') || (ch == '[') ) {
levelCurrent++;
} else if ( (ch == '}') || (ch == ')') || (ch == ']') ) {
levelCurrent--;
}
}
}
if (atEOL) {
int lev = levelPrev;
//~ if (visibleChars == 0 && foldCompact)
//~ lev |= SC_FOLDLEVELWHITEFLAG;
//~ if ((levelCurrent > levelPrev) && (visibleChars > 0))
if ((levelCurrent > levelPrev)) {
lev |= SC_FOLDLEVELHEADERFLAG;
}
if (lev != styler.LevelAt(lineCurrent)) {
styler.SetLevel(lineCurrent, lev);
}
lineCurrent++;
levelPrev = levelCurrent;
//~ visibleChars = 0;
}
//~ if (!isspacechar(ch))
//~ visibleChars++;
}
// Fill in the real level of the next line, keeping the current flags as they will be filled in later
int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
styler.SetLevel(lineCurrent, levelPrev | flagsNext);
}
static const char * const erlangWordListDesc[] = {
"Keywords",
0
};
LexerModule lmErlang(
SCLEX_ERLANG,
ColouriseErlangDoc,
"erlang",
FoldErlangDoc,
erlangWordListDesc);