diff --git a/Makefile b/Makefile index 37a3faf..ff20acf 100644 --- a/Makefile +++ b/Makefile @@ -119,7 +119,7 @@ OBJS+= charsetjis.o charsetmore.o ifdef CONFIG_ALL_MODES OBJS+= unihex.o bufed.o clang.o xml.o htmlsrc.o forth.o arm.o \ lisp.o makemode.o markdown.o orgmode.o perl.o script.o \ - ebnf.o cobol.o extra-modes.o + ebnf.o cobol.o rlang.o $(EXTRA_MODES) extra-modes.o ifndef CONFIG_WIN32 OBJS+= shell.o dired.o latex-mode.o archive.o endif diff --git a/rlang.c b/rlang.c new file mode 100644 index 0000000..d233753 --- /dev/null +++ b/rlang.c @@ -0,0 +1,203 @@ +/* + * R language mode for QEmacs. + * + * Copyright (c) 2015 Charlie Gordon. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "qe.h" + +/*---------------- R coloring ----------------*/ + +#define MAX_KEYWORD_SIZE 16 + +static char const r_keywords[] = { + "|if|else|for|in|while|repeat|next|break|switch|function|..." + "|NA_integer_|NA_real_|NA_complex_|NA_character_" + /* predefined constants */ + "|FALSE|TRUE|NULL|NA|Inf|NaN" +}; + +static char const r_types[] = { + "|" +}; + +enum { + R_STYLE_TEXT = QE_STYLE_DEFAULT, + R_STYLE_PREPROCESS = QE_STYLE_PREPROCESS, + R_STYLE_COMMENT = QE_STYLE_COMMENT, + R_STYLE_STRING = QE_STYLE_STRING, + R_STYLE_NUMBER = QE_STYLE_NUMBER, + R_STYLE_KEYWORD = QE_STYLE_KEYWORD, + R_STYLE_TYPE = QE_STYLE_TYPE, + R_STYLE_FUNCTION = QE_STYLE_FUNCTION, + R_STYLE_SYMBOL = QE_STYLE_VARIABLE, + R_STYLE_ARGDEF = QE_STYLE_VARIABLE, + R_STYLE_ARGNAME = QE_STYLE_TYPE, +}; + +enum { + R_LEVEL_MAX = 15, + IN_R_LEVEL = 0x0F, + IN_R_FUNCLEVEL = 0x70, + R_FUNCLEVEL_SHIFT = 4, + IN_R_ARGLIST = 0x80, +}; + +static void r_colorize_line(QEColorizeContext *cp, + unsigned int *str, int n, ModeDef *syn) +{ + char keyword[MAX_KEYWORD_SIZE]; + int i = 0, j, start, c, delim, style, len, level, funclevel; + int colstate = cp->colorize_state; + + level = colstate & IN_R_LEVEL; + funclevel = (colstate & IN_R_FUNCLEVEL) >> R_FUNCLEVEL_SHIFT; + style = 0; + while (i < n) { + start = i; + c = str[i++]; + switch (c) { + case '#': + if (ustrstart(str + i, "line", NULL) && !qe_isalnum(str[i + 4])) + style = R_STYLE_PREPROCESS; + else + style = R_STYLE_COMMENT; + i = n; + break; + case '\'': + case '\"': + case '`': + delim = c; + while (i < n) { + if ((c = str[i++]) == delim) + break; + if (c == '\\' && i < n) + i++; + } + style = (delim == '`') ? R_STYLE_SYMBOL : R_STYLE_STRING; + break; + case '%': + for (j = i; qe_isalpha(str[j]); j++) + continue; + if (str[j] == '%') { + i = j + 1; + style = R_STYLE_KEYWORD; + break; + } + continue; + case '(': + level++; + if (level == funclevel) + colstate |= IN_R_ARGLIST; + continue; + case ')': + if (level) + level--; + if (level < funclevel) + funclevel = 0; + colstate &= ~IN_R_ARGLIST; + continue; + case ',': + if (funclevel && level == funclevel) + colstate |= IN_R_ARGLIST; + continue; + case '=': + colstate &= ~IN_R_ARGLIST; + continue; + case 0x00A0: /* non breaking space */ + case 0x3000: /* ideographic space */ + continue; + default: + /* parse numbers */ + if (qe_isdigit(c) || (c == '.' && qe_isdigit(str[i]))) { + for (; i < n; i++) { + /* should parse actual syntax */ + if (!qe_isalnum(str[i]) && str[i] != '.' && str[i] != '+' && str[i] != '-') + break; + } + style = R_STYLE_NUMBER; + break; + } + /* parse identifiers and keywords */ + if (qe_isalpha_(c) || c == '.') { + len = 0; + keyword[len++] = (c < 0xFF) ? c : 0xFF; + for (; i < n; i++) { + if (qe_isalnum_(str[i]) || str[i] == '.') { + if (len < countof(keyword) - 1) + keyword[len++] = (str[i] < 0xFF) ? str[i] : 0xFF; + } else { + break; + } + } + keyword[len] = '\0'; + for (j = i; qe_isspace(str[j]); j++) + continue; + if (strfind(syn->keywords, keyword)) { + if (strequal(keyword, "function")) + funclevel = level + 1; + style = R_STYLE_KEYWORD; + break; + } + if (strfind(syn->types, keyword)) { + style = R_STYLE_TYPE; + break; + } + if (colstate & IN_R_ARGLIST) { + style = R_STYLE_ARGDEF; + break; + } + if (str[j] == '=') { + style = R_STYLE_ARGNAME; + break; + } + if (str[j] == '(') { + style = R_STYLE_FUNCTION; + break; + } + //style = R_STYLE_IDENTIFIER; + break; + } + continue; + } + if (style) { + SET_COLOR(str, start, i, style); + style = 0; + } + } + colstate &= ~(IN_R_LEVEL | IN_R_FUNCLEVEL); + colstate |= clamp(level, 0, R_LEVEL_MAX); + colstate |= funclevel << R_FUNCLEVEL_SHIFT; + cp->colorize_state = colstate; +} + +static ModeDef r_mode = { + .name = "R", + .extensions = "R", + .keywords = r_keywords, + .types = r_types, + .colorize_func = r_colorize_line, +}; + +static int r_init(void) +{ + qe_register_mode(&r_mode, MODEF_SYNTAX); + + return 0; +} + +qe_module_init(r_init);