|
|
@@ -0,0 +1,513 @@
|
|
|
+diff --git a/grammar.js b/grammar.js
|
|
|
+index 6d48b47..e700a18 100644
|
|
|
+--- a/grammar.js
|
|
|
++++ b/grammar.js
|
|
|
+@@ -19,6 +19,7 @@ module.exports = grammar({
|
|
|
+ $._LINE_COMMENT,
|
|
|
+ $.comment_entry,
|
|
|
+ $._multiline_string,
|
|
|
++ $._exec_block,
|
|
|
+ ],
|
|
|
+
|
|
|
+ extras: $ => [
|
|
|
+@@ -29,20 +30,30 @@ module.exports = grammar({
|
|
|
+ $._LINE_COMMENT_ALIAS,
|
|
|
+ $.copy_statement,
|
|
|
+ $.comment,
|
|
|
++ $.directive,
|
|
|
+ ],
|
|
|
+
|
|
|
+ rules: {
|
|
|
+- start: $ => repeat(
|
|
|
+- choice(
|
|
|
+- $.program_definition,
|
|
|
+- //optional($.function_definition) //todo
|
|
|
+- )
|
|
|
++ start: $ => optional(choice(
|
|
|
++ repeat1($.program_definition),
|
|
|
++ $.copybook_fragment,
|
|
|
++ //optional($.function_definition) //todo
|
|
|
++ )),
|
|
|
++
|
|
|
++ // A standalone copybook (.cpy): data descriptions or procedure code
|
|
|
++ // without a program header.
|
|
|
++ copybook_fragment: $ => choice(
|
|
|
++ $.record_description_list,
|
|
|
++ $._procedure_division_contenet
|
|
|
+ ),
|
|
|
+
|
|
|
+ _LINE_COMMENT_ALIAS: $ => alias($._LINE_COMMENT, $.comment),
|
|
|
+
|
|
|
+ comment: $ => /\*>[^\n]*/,
|
|
|
+
|
|
|
++ // GnuCOBOL / COBOL-2002 compiler directives: >>SOURCE, >>IF, >>DEFINE, ...
|
|
|
++ directive: $ => />>[^\n]*/,
|
|
|
++
|
|
|
+ program_definition: $ => prec.right(seq(
|
|
|
+ $.identification_division,
|
|
|
+ optional($.environment_division),
|
|
|
+@@ -60,7 +71,8 @@ module.exports = grammar({
|
|
|
+ optional(choice(
|
|
|
+ $.as_literal,
|
|
|
+ $.is_initial,
|
|
|
+- $.is_common)),
|
|
|
++ $.is_common,
|
|
|
++ $.is_recursive)),
|
|
|
+ '.')),
|
|
|
+ repeat(choice(
|
|
|
+ $.author_section,
|
|
|
+@@ -90,6 +102,11 @@ module.exports = grammar({
|
|
|
+ $._COMMON
|
|
|
+ ),
|
|
|
+
|
|
|
++ is_recursive: $ => seq(
|
|
|
++ optional($._IS),
|
|
|
++ $._RECURSIVE
|
|
|
++ ),
|
|
|
++
|
|
|
+ author_section: $ => seq(
|
|
|
+ $._AUTHOR, '.',
|
|
|
+ field('comment', repeat1($.comment_entry)),
|
|
|
+@@ -102,17 +119,17 @@ module.exports = grammar({
|
|
|
+
|
|
|
+ date_written_section: $ => seq(
|
|
|
+ $._DATE_WRITTEN, '.',
|
|
|
+- field('comment', repeat1($.comment_entry)),
|
|
|
++ field('comment', repeat($.comment_entry)),
|
|
|
+ ),
|
|
|
+
|
|
|
+ date_compiled_section: $ => seq(
|
|
|
+ $._DATE_COMPILED, '.',
|
|
|
+- field('comment', repeat1($.comment_entry)),
|
|
|
++ field('comment', repeat($.comment_entry)),
|
|
|
+ ),
|
|
|
+
|
|
|
+ security_section: $ => seq(
|
|
|
+ $._SECURITY, '.',
|
|
|
+- field('comment', repeat1($.comment_entry)),
|
|
|
++ field('comment', repeat($.comment_entry)),
|
|
|
+ ),
|
|
|
+
|
|
|
+ function_definition: $ => seq(
|
|
|
+@@ -734,7 +751,7 @@ module.exports = grammar({
|
|
|
+ file_description: $ => seq(
|
|
|
+ $.file_type,
|
|
|
+ $.file_description_entry,
|
|
|
+- $.record_description_list
|
|
|
++ optional($.record_description_list)
|
|
|
+ ),
|
|
|
+
|
|
|
+ file_type: $ => choice(
|
|
|
+@@ -870,12 +887,12 @@ module.exports = grammar({
|
|
|
+ ),
|
|
|
+
|
|
|
+ record_description_list: $ => seq(
|
|
|
+- repeat1(seq($.data_description, repeat1('.')))
|
|
|
++ repeat1(choice(seq($.data_description, repeat1('.')), prec(1, seq($.exec_statement, optional('.')))))
|
|
|
+ ),
|
|
|
+
|
|
|
+ working_storage_section: $ => seq(
|
|
|
+ $._WORKING_STORAGE, $._SECTION, '.',
|
|
|
+- repeat(seq($.data_description, repeat1('.')))
|
|
|
++ repeat(choice(seq($.data_description, repeat1('.')), seq($.exec_statement, optional('.'))))
|
|
|
+ ),
|
|
|
+
|
|
|
+ data_description: $ => choice(
|
|
|
+@@ -1070,7 +1087,7 @@ module.exports = grammar({
|
|
|
+ $.picture_edit,
|
|
|
+ ),
|
|
|
+
|
|
|
+- picture_x: $ => /([xX](\([0-9]+\))?)+/,
|
|
|
++ picture_x: $ => /([xX](\(([0-9]+|[a-zA-Z][a-zA-Z0-9-]*)\))?)+/,
|
|
|
+
|
|
|
+ picture_n: $ => /([nN](\([0-9]+\))?)+/,
|
|
|
+
|
|
|
+@@ -1119,6 +1136,11 @@ module.exports = grammar({
|
|
|
+ seq($.BINARY_SHORT, $.SIGNED),
|
|
|
+ seq($.BINARY_SHORT, $.UNSIGNED),
|
|
|
+ $.BINARY_SHORT,
|
|
|
++ seq($.BINARY_LONG_LONG, $.SIGNED),
|
|
|
++ seq($.BINARY_LONG_LONG, $.UNSIGNED),
|
|
|
++ $.BINARY_LONG_LONG,
|
|
|
++ $.FLOAT_LONG,
|
|
|
++ $.FLOAT_SHORT,
|
|
|
+ seq($.BINARY_LONG, $.SIGNED),
|
|
|
+ seq($.BINARY_LONG, $.UNSIGNED),
|
|
|
+ $.BINARY_LONG,
|
|
|
+@@ -1197,7 +1219,7 @@ module.exports = grammar({
|
|
|
+ )),
|
|
|
+
|
|
|
+ value_item: $ => seq(
|
|
|
+- $._literal,
|
|
|
++ choice($._literal, $.qualified_word),
|
|
|
+ optional(seq(
|
|
|
+ $.THRU,
|
|
|
+ $._literal
|
|
|
+@@ -1227,7 +1249,7 @@ module.exports = grammar({
|
|
|
+
|
|
|
+ linkage_section: $ => seq(
|
|
|
+ $._LINKAGE, $._SECTION, '.',
|
|
|
+- $.record_description_list
|
|
|
++ optional($.record_description_list)
|
|
|
+ ),
|
|
|
+
|
|
|
+ report_section: $ => /report_section/,
|
|
|
+@@ -1357,6 +1379,19 @@ module.exports = grammar({
|
|
|
+ '.'
|
|
|
+ ),
|
|
|
+
|
|
|
++ exec_statement: $ => $._exec_block,
|
|
|
++
|
|
|
++ free_statement: $ => prec.right(seq(
|
|
|
++ $._FREE,
|
|
|
++ repeat1($._target_x)
|
|
|
++ )),
|
|
|
++
|
|
|
++ entry_statement: $ => prec.right(seq(
|
|
|
++ $._ENTRY,
|
|
|
++ field('name', $.string),
|
|
|
++ optional(seq($._USING, repeat1($._call_param)))
|
|
|
++ )),
|
|
|
++
|
|
|
+ end_program: $ => prec(1, seq(
|
|
|
+ $._END_PROGRAM,
|
|
|
+ $.program_name,
|
|
|
+@@ -1364,6 +1399,9 @@ module.exports = grammar({
|
|
|
+ )),
|
|
|
+
|
|
|
+ _statement: $ => choice(
|
|
|
++ $.exec_statement,
|
|
|
++ $.entry_statement,
|
|
|
++ $.free_statement,
|
|
|
+ $.accept_statement,
|
|
|
+ $.add_statement,
|
|
|
+ $.allocate_statement,
|
|
|
+@@ -1465,12 +1503,19 @@ module.exports = grammar({
|
|
|
+ ),
|
|
|
+
|
|
|
+ replacing_clause: $ => seq(
|
|
|
++ $._REPLACING,
|
|
|
++ repeat1($.replacing_pair)
|
|
|
++ ),
|
|
|
++
|
|
|
++ replacing_pair: $ => seq(
|
|
|
+ field('leading_or_trailing', optional(choice($.LEADING, $.TRAILING))),
|
|
|
+- field('x', choice($.WORD, $.string)),
|
|
|
+- optional($._BY),
|
|
|
+- field('by', choice($.WORD, $.string)),
|
|
|
++ field('x', choice($.pseudo_text, $.WORD, $.string)),
|
|
|
++ $._BY,
|
|
|
++ field('by', choice($.pseudo_text, $.WORD, $.string)),
|
|
|
+ ),
|
|
|
+
|
|
|
++ pseudo_text: $ => /==([^=\n]|=[^=\n])*==/,
|
|
|
++
|
|
|
+ start_statement: $ => seq(
|
|
|
+ $._START,
|
|
|
+ field('file_name', $.WORD),
|
|
|
+@@ -1670,9 +1715,9 @@ module.exports = grammar({
|
|
|
+ repeat1($._call_param)
|
|
|
+ ))),
|
|
|
+ optional(choice(
|
|
|
+- field('returning', seq($._RETURNING, $._identifier),
|
|
|
+- field('giving', seq($._GIVING, $._identifier)),
|
|
|
+- )))
|
|
|
++ field('returning', seq($._RETURNING, $._identifier)),
|
|
|
++ field('giving', seq($._GIVING, $._identifier))
|
|
|
++ ))
|
|
|
+ ),
|
|
|
+
|
|
|
+ _call_param: $ => choice(
|
|
|
+@@ -1902,6 +1947,7 @@ module.exports = grammar({
|
|
|
+
|
|
|
+ _evaluate_object: $ => choice(
|
|
|
+ $.expr,
|
|
|
++ prec.right(seq(choice('=', '>', '<', '>=', '<=', $._NOT_EQUAL), $.expr)),
|
|
|
+ $.ANY,
|
|
|
+ $.TRUE,
|
|
|
+ $.FALSE
|
|
|
+@@ -1951,6 +1997,10 @@ module.exports = grammar({
|
|
|
+ expr: $ => prec.left(choice(
|
|
|
+ seq($.NOT, $.expr),
|
|
|
+ seq($.expr, choice($.AND, $.OR), $.expr),
|
|
|
++ // Abbreviated combined relation: `X < 0 OR > Y` — the subject of the
|
|
|
++ // second comparison is implied. (The AND_LT/OR_GT combined tokens the
|
|
|
++ // grammar carries for this never win against keyword lexing.)
|
|
|
++ seq($.expr, choice($.AND, $.OR), $._comparator, $._expr_calc),
|
|
|
+ $._expr_bool,
|
|
|
+ seq("(", $.expr, ")")
|
|
|
+ )),
|
|
|
+@@ -1965,6 +2015,11 @@ module.exports = grammar({
|
|
|
+ )),
|
|
|
+
|
|
|
+ _expr_calc_binary: $ => choice(
|
|
|
++ prec.left(1, seq($._expr_calc, $.B_AND, $._expr_calc)),
|
|
|
++ prec.left(1, seq($._expr_calc, $.B_OR, $._expr_calc)),
|
|
|
++ prec.left(1, seq($._expr_calc, $.B_XOR, $._expr_calc)),
|
|
|
++ prec.left(1, seq($._expr_calc, $.B_SHIFT_L, $._expr_calc)),
|
|
|
++ prec.left(1, seq($._expr_calc, $.B_SHIFT_R, $._expr_calc)),
|
|
|
+ prec.left(1, seq($._expr_calc, '+', $._expr_calc)),
|
|
|
+ prec.left(1, seq($._expr_calc, '-', $._expr_calc)),
|
|
|
+ prec.left(2, seq($._expr_calc, '**', $._expr_calc)),
|
|
|
+@@ -1974,6 +2029,7 @@ module.exports = grammar({
|
|
|
+ ),
|
|
|
+
|
|
|
+ _expr_calc_unary: $ => prec(4, choice(
|
|
|
++ seq($.B_NOT, $._expr_calc),
|
|
|
+ seq('+', $._expr_calc),
|
|
|
+ seq('-', $._expr_calc),
|
|
|
+ seq('^', $._expr_calc),
|
|
|
+@@ -2753,7 +2809,7 @@ module.exports = grammar({
|
|
|
+ seq($.TRIM_FUNCTION, '(', $._trim_args, ')', optional($.func_refmod)),
|
|
|
+ seq($.NUMVALC_FUNC, '(', $._numvalc_args, ')'),
|
|
|
+ seq($.LOCALE_DT_FUNC, '(', $._locale_dt_args, ')', optional($.func_refmod)),
|
|
|
+- seq($.WORD, optional($.func_args)),
|
|
|
++ seq($.WORD, optional($.func_args), optional($.func_refmod)),
|
|
|
+ ))),
|
|
|
+
|
|
|
+ func_refmod: $ => choice(
|
|
|
+@@ -2868,6 +2924,16 @@ module.exports = grammar({
|
|
|
+ _BINARY_CHAR: $ => /[bB][iI][nN][aA][rR][yY]-[cC][hH][aA][rR]/,
|
|
|
+ _BINARY_DOUBLE: $ => /[bB][iI][nN][aA][rR][yY]-[dD][oO][uU][bB][lL][eE]/,
|
|
|
+ _BINARY_LONG: $ => /[bB][iI][nN][aA][rR][yY]-[lL][oO][nN][gG]/,
|
|
|
++ _BINARY_LONG_LONG: $ => /[bB][iI][nN][aA][rR][yY]-[lL][oO][nN][gG]-[lL][oO][nN][gG]/,
|
|
|
++ _FREE: $ => /[fF][rR][eE][eE]/,
|
|
|
++ B_AND: $ => /[bB]-[aA][nN][dD]/,
|
|
|
++ B_OR: $ => /[bB]-[oO][rR]/,
|
|
|
++ B_XOR: $ => /[bB]-[xX][oO][rR]/,
|
|
|
++ B_NOT: $ => /[bB]-[nN][oO][tT]/,
|
|
|
++ B_SHIFT_L: $ => /[bB]-[sS][hH][iI][fF][tT]-[lL][cC]?/,
|
|
|
++ B_SHIFT_R: $ => /[bB]-[sS][hH][iI][fF][tT]-[rR][cC]?/,
|
|
|
++ _FLOAT_LONG: $ => /[fF][lL][oO][aA][tT]-[lL][oO][nN][gG]/,
|
|
|
++ _FLOAT_SHORT: $ => /[fF][lL][oO][aA][tT]-[sS][hH][oO][rR][tT]/,
|
|
|
+ _BINARY_SHORT: $ => /[bB][iI][nN][aA][rR][yY]-[sS][hH][oO][rR][tT]/,
|
|
|
+ _BLANK: $ => /[bB][lL][aA][nN][kK]/,
|
|
|
+ _BLANK_LINE: $ => /[bB][lL][aA][nN][kK]-[lL][iI][nN][eE]/,
|
|
|
+@@ -3335,6 +3401,9 @@ module.exports = grammar({
|
|
|
+ BINARY_CHAR: $ => $._BINARY_CHAR,
|
|
|
+ BINARY_DOUBLE: $ => $._BINARY_DOUBLE,
|
|
|
+ BINARY_LONG: $ => $._BINARY_LONG,
|
|
|
++ BINARY_LONG_LONG: $ => $._BINARY_LONG_LONG,
|
|
|
++ FLOAT_LONG: $ => $._FLOAT_LONG,
|
|
|
++ FLOAT_SHORT: $ => $._FLOAT_SHORT,
|
|
|
+ BINARY_SHORT: $ => $._BINARY_SHORT,
|
|
|
+ //BLANK: $ => $._BLANK,
|
|
|
+ BLANK_LINE: $ => $._BLANK_LINE,
|
|
|
+@@ -3754,7 +3823,7 @@ module.exports = grammar({
|
|
|
+
|
|
|
+ COMPUTATIONAL: $ => $._COMPUTATIONAL,
|
|
|
+ _COMPUTATIONAL: $ => /[cC][oO][mM][pP][uU][tT][aA][tT][iI][oO][nN][aA][lL]/,
|
|
|
+- _NOT_EQUAL: $ => /(!=)|([nN][oO][tT][ \t]+(([eE][qQ][uU][aA][lL])|=))/,
|
|
|
++ _NOT_EQUAL: $ => /(!=)|([nN][oO][tT]([ \t]+[eE][qQ][uU][aA][lL]|[ \t]*=))/,
|
|
|
+ _NOT_LESS: $ => /([nN][oO][tT][ \t]+(<|[lL][eE][sS][sS]))/,
|
|
|
+ _NOT_GREATER: $ => /([nN][oO][tT][ \t]+(>|[gG][rR][eE][aA][tT][eE][rR]))/,
|
|
|
+
|
|
|
+diff --git a/src/scanner.c b/src/scanner.c
|
|
|
+index 6c1ae90..b69cf53 100644
|
|
|
+--- a/src/scanner.c
|
|
|
++++ b/src/scanner.c
|
|
|
+@@ -1,4 +1,5 @@
|
|
|
+ #include <tree_sitter/parser.h>
|
|
|
++#include <stdlib.h>
|
|
|
+ #include <wctype.h>
|
|
|
+
|
|
|
+ enum TokenType {
|
|
|
+@@ -8,10 +9,21 @@ enum TokenType {
|
|
|
+ LINE_COMMENT,
|
|
|
+ COMMENT_ENTRY,
|
|
|
+ multiline_string,
|
|
|
++ EXEC_BLOCK,
|
|
|
+ };
|
|
|
+
|
|
|
++// Wide mode: a preprocessor that converts free-format COBOL to fixed format
|
|
|
++// by left-padding every line 7 columns writes the sentinel "CGWIDE" into the
|
|
|
++// sequence area (columns 1-6) of the FIRST line. Free-format lines routinely
|
|
|
++// run past column 72, so when the sentinel is seen the fixed-format right
|
|
|
++// margin (column 73+ = ignored identification area) is pushed out of reach.
|
|
|
++// The one-byte flag is scanner state, carried through serialize/deserialize.
|
|
|
++#define CG_FIXED_WIDTH 72
|
|
|
++#define CG_WIDE_WIDTH 4096
|
|
|
++static const char CG_WIDE_SENTINEL[6] = {'C', 'G', 'W', 'I', 'D', 'E'};
|
|
|
++
|
|
|
+ void *tree_sitter_COBOL_external_scanner_create() {
|
|
|
+- return NULL;
|
|
|
++ return calloc(1, 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool is_white_space(int c) {
|
|
|
+@@ -31,7 +43,7 @@ char* any_content_keyword[] = {
|
|
|
+ "procedure division",
|
|
|
+ };
|
|
|
+
|
|
|
+-static bool start_with_word( TSLexer *lexer, char *words[], int number_of_words) {
|
|
|
++static bool start_with_word( TSLexer *lexer, char *words[], int number_of_words, int width) {
|
|
|
+ while(lexer->lookahead == ' ' || lexer->lookahead == '\t') {
|
|
|
+ lexer->advance(lexer, true);
|
|
|
+ }
|
|
|
+@@ -45,7 +57,7 @@ static bool start_with_word( TSLexer *lexer, char *words[], int number_of_words)
|
|
|
+
|
|
|
+ while(true) {
|
|
|
+ // At the end of the line
|
|
|
+- if(lexer->get_column(lexer) > 71 || lexer->lookahead == '\n' || lexer->lookahead == 0) {
|
|
|
++ if(lexer->get_column(lexer) > width - 1 || lexer->lookahead == '\n' || lexer->lookahead == 0) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -58,7 +70,7 @@ static bool start_with_word( TSLexer *lexer, char *words[], int number_of_words)
|
|
|
+ }
|
|
|
+
|
|
|
+ if(all_match_failed) {
|
|
|
+- for(; lexer->get_column(lexer) < 71 && lexer->lookahead != '\n' && lexer->lookahead != 0;
|
|
|
++ for(; lexer->get_column(lexer) < width - 1 && lexer->lookahead != '\n' && lexer->lookahead != 0;
|
|
|
+ lexer->advance(lexer, true)) {
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+@@ -94,6 +106,9 @@ bool tree_sitter_COBOL_external_scanner_scan(void *payload, TSLexer *lexer,
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
++ char *wide = (char *)payload;
|
|
|
++ const int width = (wide && *wide) ? CG_WIDE_WIDTH : CG_FIXED_WIDTH;
|
|
|
++
|
|
|
+ if(valid_symbols[WHITE_SPACES]) {
|
|
|
+ if(is_white_space(lexer->lookahead)) {
|
|
|
+ while(is_white_space(lexer->lookahead)) {
|
|
|
+@@ -106,9 +121,20 @@ bool tree_sitter_COBOL_external_scanner_scan(void *payload, TSLexer *lexer,
|
|
|
+ }
|
|
|
+
|
|
|
+ if(valid_symbols[LINE_PREFIX_COMMENT] && lexer->get_column(lexer) <= 5) {
|
|
|
++ // The sequence area is ignored content — but the free-format
|
|
|
++ // preprocessor plants the CGWIDE sentinel here on the first line.
|
|
|
++ int match = 0;
|
|
|
+ while(lexer->get_column(lexer) <= 5) {
|
|
|
++ if(match >= 0 && match < 6 && lexer->lookahead == CG_WIDE_SENTINEL[match]) {
|
|
|
++ match++;
|
|
|
++ } else {
|
|
|
++ match = -1;
|
|
|
++ }
|
|
|
+ lexer->advance(lexer, true);
|
|
|
+ }
|
|
|
++ if(match == 6 && wide) {
|
|
|
++ *wide = 1;
|
|
|
++ }
|
|
|
+ lexer->result_symbol = LINE_PREFIX_COMMENT;
|
|
|
+ lexer->mark_end(lexer);
|
|
|
+ return true;
|
|
|
+@@ -132,7 +158,7 @@ bool tree_sitter_COBOL_external_scanner_scan(void *payload, TSLexer *lexer,
|
|
|
+ }
|
|
|
+
|
|
|
+ if(valid_symbols[LINE_SUFFIX_COMMENT]) {
|
|
|
+- if(lexer->get_column(lexer) >= 72) {
|
|
|
++ if(lexer->get_column(lexer) >= width) {
|
|
|
+ while(lexer->lookahead != '\n' && lexer->lookahead != 0) {
|
|
|
+ lexer->advance(lexer, true);
|
|
|
+ }
|
|
|
+@@ -143,7 +169,7 @@ bool tree_sitter_COBOL_external_scanner_scan(void *payload, TSLexer *lexer,
|
|
|
+ }
|
|
|
+
|
|
|
+ if(valid_symbols[COMMENT_ENTRY]) {
|
|
|
+- if(!start_with_word(lexer, any_content_keyword, number_of_comment_entry_keywords)) {
|
|
|
++ if(!start_with_word(lexer, any_content_keyword, number_of_comment_entry_keywords, width)) {
|
|
|
+ lexer->mark_end(lexer);
|
|
|
+ lexer->result_symbol = COMMENT_ENTRY;
|
|
|
+ return true;
|
|
|
+@@ -152,18 +178,66 @@ bool tree_sitter_COBOL_external_scanner_scan(void *payload, TSLexer *lexer,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
++ if(valid_symbols[EXEC_BLOCK]) {
|
|
|
++ // EXEC (CICS|SQL|DLI|...) ... END-EXEC embedded block. Match the word
|
|
|
++ // EXEC (case-insensitive) followed by whitespace, then consume through
|
|
|
++ // the next END-EXEC. On any mismatch return false so the internal
|
|
|
++ // lexer re-reads the same characters as an ordinary WORD.
|
|
|
++ if(lexer->lookahead == 'e' || lexer->lookahead == 'E') {
|
|
|
++ const char *kw = "exec";
|
|
|
++ int ki = 0;
|
|
|
++ while(ki < 4 && (lexer->lookahead == towupper(kw[ki]) || lexer->lookahead == towlower(kw[ki]))) {
|
|
|
++ lexer->advance(lexer, false);
|
|
|
++ ki++;
|
|
|
++ }
|
|
|
++ if(ki == 4 && (lexer->lookahead == ' ' || lexer->lookahead == '\t' ||
|
|
|
++ lexer->lookahead == '\n' || lexer->lookahead == '\r')) {
|
|
|
++ char ring[8] = {0,0,0,0,0,0,0,0};
|
|
|
++ while(lexer->lookahead != 0) {
|
|
|
++ for(int i = 0; i < 7; ++i) ring[i] = ring[i+1];
|
|
|
++ ring[7] = (char)towlower(lexer->lookahead);
|
|
|
++ lexer->advance(lexer, false);
|
|
|
++ if(ring[0]=='e' && ring[1]=='n' && ring[2]=='d' && ring[3]=='-' &&
|
|
|
++ ring[4]=='e' && ring[5]=='x' && ring[6]=='e' && ring[7]=='c') {
|
|
|
++ lexer->result_symbol = EXEC_BLOCK;
|
|
|
++ lexer->mark_end(lexer);
|
|
|
++ return true;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ }
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
+ if(valid_symbols[multiline_string]) {
|
|
|
++ int quote = lexer->lookahead;
|
|
|
++ if(quote != '"' && quote != '\'') {
|
|
|
++ return false;
|
|
|
++ }
|
|
|
+ while(true) {
|
|
|
+- if(lexer->lookahead != '"') {
|
|
|
++ if(lexer->lookahead != quote) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ lexer->advance(lexer, false);
|
|
|
+- while(lexer->lookahead != '"' && lexer->lookahead != 0 && lexer->get_column(lexer) < 72) {
|
|
|
++ bool closed = false;
|
|
|
++ while(true) {
|
|
|
++ while(lexer->lookahead != quote && lexer->lookahead != 0 && lexer->get_column(lexer) < width) {
|
|
|
++ lexer->advance(lexer, false);
|
|
|
++ }
|
|
|
++ if(lexer->lookahead != quote) {
|
|
|
++ break;
|
|
|
++ }
|
|
|
+ lexer->advance(lexer, false);
|
|
|
++ if(lexer->lookahead == quote) {
|
|
|
++ // doubled quote = escaped quote inside the literal
|
|
|
++ lexer->advance(lexer, false);
|
|
|
++ continue;
|
|
|
++ }
|
|
|
++ closed = true;
|
|
|
++ break;
|
|
|
+ }
|
|
|
+- if(lexer->lookahead == '"') {
|
|
|
++ if(closed) {
|
|
|
+ lexer->result_symbol = multiline_string;
|
|
|
+- lexer->advance(lexer, false);
|
|
|
+ lexer->mark_end(lexer);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+@@ -187,7 +261,7 @@ bool tree_sitter_COBOL_external_scanner_scan(void *payload, TSLexer *lexer,
|
|
|
+ }
|
|
|
+
|
|
|
+ lexer->advance(lexer, true);
|
|
|
+- while(lexer->lookahead == ' ' && lexer->get_column(lexer) < 72) {
|
|
|
++ while(lexer->lookahead == ' ' && lexer->get_column(lexer) < width) {
|
|
|
+ lexer->advance(lexer, true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+@@ -197,11 +271,19 @@ bool tree_sitter_COBOL_external_scanner_scan(void *payload, TSLexer *lexer,
|
|
|
+ }
|
|
|
+
|
|
|
+ unsigned tree_sitter_COBOL_external_scanner_serialize(void *payload, char *buffer) {
|
|
|
++ if(payload && buffer) {
|
|
|
++ buffer[0] = *(char *)payload;
|
|
|
++ return 1;
|
|
|
++ }
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ void tree_sitter_COBOL_external_scanner_deserialize(void *payload, const char *buffer, unsigned length) {
|
|
|
++ if(payload) {
|
|
|
++ *(char *)payload = (buffer && length >= 1) ? buffer[0] : 0;
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void tree_sitter_COBOL_external_scanner_destroy(void *payload) {
|
|
|
++ free(payload);
|
|
|
+ }
|