diff --git a/Makefile b/Makefile index f8dde8f..37a3faf 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 extra-modes.o + ebnf.o cobol.o extra-modes.o ifndef CONFIG_WIN32 OBJS+= shell.o dired.o latex-mode.o archive.o endif diff --git a/cobol.c b/cobol.c new file mode 100644 index 0000000..150c821 --- /dev/null +++ b/cobol.c @@ -0,0 +1,257 @@ +/* + * Cobol 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" + +/*---------------- COBOL coloring ----------------*/ + +#define COBOL_KEYWORD_SIZE 24 + +static char const cobol_keywords[] = { + "|identification|procedure|data|division|program-id" + "|section|working-storage|environment|configuration|input-output" + "|source-computer|object-computer|special-names" + "|if|else|end-if|of|is|equal|less|greater|than|to|into|not|or|and" + "|compute|end-compute|call|end-call|using|length|rounded" + "|move|set|up|down|address" + "|add|end-add|subtract|end-subtract" + "|multiply|end-multiply" + "|divide|by|giving|remainder|end-divide" + "|perform|end-perform|varying|from|until|thru|after" + "|on|size|error|exit|initialize|continue" + "|evaluate|when|other|end-evaluate" + "|display|at|line|column|plus|minus|with|highlight|lowlight" + "|background-color|foreground-color|reverse-video|blink" + "|with|no|advancing|upon|end-display" + "|function|end|program" + "|stop|run|returning" + "|filler|value|values|occurs|times|redefines" + "|constant|as" + "|accept|end-accept" + "|goback|go|to|depending|on" + "|copy|inspect|replacing|converting|leading|trailing|to|tallying" + "|first|last|for|all|by" + "|string|end-string|unstring|end-unstring|delimited|by|into" + "|open|input|output" + "|close" + "|read|write" + "|select|assign|organization|line|sequential|status" + "|next|sentence" + "|usage|any|length" +}; + +static char const cobol_types[] = { + "|fd|pic|zero|zeros|zeroes|space|spaces|true|false" + //"|byteint|char|float|display-numeric" + "|group|native|binary|pointer|binary-char|binary-long|unsigned" + "|character|date|decimal|graphic|integer|numeric|smallint" + "|time|timestamp|varchar|vargraphic" + "|comp|comp-1|comp-2|comp-3|comp-4|comp-5|comp-6|display-1" +}; + +enum { + COBOL_STYLE_TEXT = QE_STYLE_DEFAULT, + COBOL_STYLE_COMMENT = QE_STYLE_COMMENT, + COBOL_STYLE_STRING = QE_STYLE_STRING, + COBOL_STYLE_KEYWORD = QE_STYLE_KEYWORD, + COBOL_STYLE_SYMBOL = QE_STYLE_NUMBER, + COBOL_STYLE_TYPE = QE_STYLE_TYPE, + COBOL_STYLE_NUMBER = QE_STYLE_NUMBER, + COBOL_STYLE_PREPROCESS = QE_STYLE_PREPROCESS, + COBOL_STYLE_HEADING = QE_STYLE_PREPROCESS, + COBOL_STYLE_IDENTIFIER = QE_STYLE_VARIABLE, +}; + +enum { + IN_COBOL_SOURCE_FORMAT = 0x03, + IN_COBOL_FIXED_FORMAT = 0x01, /* source format is fixed */ + IN_COBOL_FREE_FORMAT = 0x02, /* source format is free */ +}; + +static void cobol_colorize_line(QEColorizeContext *cp, + unsigned int *str, int n, ModeDef *syn) +{ + char keyword[COBOL_KEYWORD_SIZE]; + int i = 0, start = i, j, c, style, len, indent, heading = 0, preproc = 0, comment = -1; + int state = cp->colorize_state; + + for (; i < n && qe_isspace(str[i]); i++) + continue; + indent = i; + + if (str[i] == '*' && str[i + 1] == '>') + comment = i; + + if (str[i] == '>') + goto check_source_format; + + if (!(state & IN_COBOL_FREE_FORMAT)) { + if (!(state & IN_COBOL_FIXED_FORMAT)) { + check_source_format: + if (comment < 0 && ustristr(str, "source") != NULL) { + if (ustristr(str, "free")) { + state &= ~IN_COBOL_FIXED_FORMAT; + state |= IN_COBOL_FREE_FORMAT; + preproc = 1; + } else + if (ustristr(str, "fixed")) { + state &= ~IN_COBOL_FREE_FORMAT; + state |= IN_COBOL_FIXED_FORMAT; + preproc = 1; + } + if (preproc) { + i = n; + SET_COLOR(str, start, i, COBOL_STYLE_PREPROCESS); + } + } else { + if (comment >= 0 && comment < 6) { + state |= IN_COBOL_FREE_FORMAT; + } else + if (i < 6 && qe_isdigit(str[i])) { + for (j = i + 1; j < n && qe_isdigit(str[j]); j++) + continue; + if (j == 6) { + heading = 6; + if (i == 0) { + state |= IN_COBOL_FIXED_FORMAT; + } + } + } + } + } + if ((state & IN_COBOL_FIXED_FORMAT) || heading || i == 6) { + i = heading = 6; + SET_COLOR(str, start, i, COBOL_STYLE_HEADING); + } + } + + style = 0; + while (i < n) { + start = i; + c = str[i++]; + switch (c) { + case '*': + if ((i == 7 && heading == 6) || str[i] == '>') { + i = n; + style = COBOL_STYLE_COMMENT; + break; + } + continue; + case '\"': + case '\'': + while (i < n && str[i++] != (unsigned int)c) + continue; + style = COBOL_STYLE_STRING; + break; + case '-': + if (qe_isdigit(str[i]) || (str[i] == '.' && qe_isdigit(str[i + 1]))) + goto number; + break; + case '.': + if (qe_isdigit(str[i])) + goto number; + break; + default: + if (qe_isdigit(c)) { + /* try and parse a number */ + number: + for (; i < n && qe_isdigit(str[i]); i++) + continue; + if (str[i] == '.' && qe_isdigit(str[i + 1])) { + for (i += 2; i < n && qe_isdigit(str[i]); i++) + continue; + } + if (i >= n || !(qe_isalnum(str[i]) || str[i] == '-')) { + style = COBOL_STYLE_NUMBER; + break; + } else { + /* restart for identifier */ + i = start; + c = str[i++]; + } + } + if (qe_isalnum_(c)) { + /* parse identifiers and keywords */ + len = 0; + keyword[len++] = qe_tolower(c); + for (; i < n; i++) { + int c = str[i]; + if (!qe_isalnum_(c) && !qe_findchar("-$", c)) + break; + if (len < countof(keyword) - 1) + keyword[len++] = qe_tolower(c); + } + keyword[len] = '\0'; + if (strfind(syn->keywords, keyword)) { + style = COBOL_STYLE_KEYWORD; + break; + } + if (strfind(syn->types, keyword)) { + style = COBOL_STYLE_TYPE; + break; + } + //style = COBOL_STYLE_IDENTIFIER; + break; + } + } + if (style) { + SET_COLOR(str, start, i, style); + style = 0; + } + } + cp->colorize_state = state; +} + +static int cobol_mode_probe(ModeDef *mode, ModeProbeData *pd) +{ + const u8 *p; + + /* trust the file extension and/or shell handler */ + if (match_extension(pd->filename, mode->extensions) + || match_shell_handler(cs8(pd->buf), mode->shell_handlers)) { + return 80; + } + /* scan for a cobol comment */ + /* XXX: need flags in ModeProbeData to verify text buffer */ + for (p = pd->buf; *p == ' '; p++) + continue; + if (p[0] == '*' && p[1] == '>') + return 60; + + return 1; +} + +static ModeDef cobol_mode = { + .name = "Cobol", + .extensions = "cbl|cob|cpy", + .keywords = cobol_keywords, + .types = cobol_types, + .mode_probe = cobol_mode_probe, + .colorize_func = cobol_colorize_line, +}; + +static int cobol_init(void) +{ + qe_register_mode(&cobol_mode, MODEF_SYNTAX); + + return 0; +} + +qe_module_init(cobol_init);