#include <stdlib.h>
#include <string.h>
#include <wctype.h>
#include "tree_sitter/parser.h"
#define MAX_WORD_LENGTH 100
enum TokenType {
DATABLOCK_START,
DATABLOCK_END,
CMD_FIT_KW, CMD_PLOT_KW, CMD_SPLOT_KW, CMD_PAUSE_KW, CMD_PRINT_KW, CMD_HELP_KW, CMD_LOAD_KW, KW_PLT_ST, KW_CMD_BARE, KW_CMD_OPTEXPR, KW_CMD_EXIT, KW_CMD_EXPR, KW_SA,
KW_LT, KW_LC, KW_DT, KW_PT, KW_PS,
KW_FS, KW_FC, KW_TC,
KW_G_ARG, KW_G_FLAG, KW_G_MOD, KW_G_COORD, KW_G_AXISFLAG,
KW_G_AXISRANGE, GVAL_SEP,
};
typedef struct {
const char* keyword;
const char* alt;
int min_chars;
} KwEntry;
static const KwEntry PLT_STYLE_KWS[] = {
{"linespoints", "lp", 6}, {"lines", NULL, 1},
{"points", NULL, 1},
{"financebars", NULL, 3},
{"dots", NULL, 1},
{"impulses", NULL, 1},
{"surface", NULL, 3},
{"steps", NULL, 2},
{"fsteps", NULL, 6},
{"histeps", NULL, 7},
{"arrows", NULL, 3},
{"sectors", NULL, 3},
{"xerrorbars", NULL, 9}, {"yerrorbars", NULL, 9},
{"xyerrorbars", NULL, 10},
{"xerrorlines", NULL, 11},
{"yerrorlines", NULL, 11},
{"xyerrorlines", NULL, 12},
{"parallelaxes", NULL, 12},
{"boxerrorbars", NULL, 12},
{"boxxyerror", NULL, 10},
{"boxplot", NULL, 7},
{"boxes", NULL, 5},
{"circles", NULL, 7},
{"zerrorfill", NULL, 6},
{"contourfill", NULL, 11},
{"spiderplot", NULL, 6},
{"histograms", NULL, 4},
{"rgbalpha", NULL, 8},
{"rgbimage", NULL, 8},
{"polygons", NULL, 8},
{"table", NULL, 5},
{"mask", NULL, 4},
{NULL, NULL, 0},
};
static bool match_kw_table(const char* word, int wlen, const KwEntry* table) {
for (int i = 0; table[i].keyword != NULL; i++) {
const KwEntry* e = &table[i];
int klen = (int)strlen(e->keyword);
if (wlen >= e->min_chars && wlen <= klen && strncmp(word, e->keyword, (size_t)wlen) == 0)
return true;
if (e->alt && strcmp(word, e->alt) == 0)
return true;
}
return false;
}
typedef struct {
const char* keyword;
const char* alt;
int min_chars;
int symbol;
} StyleKwEntry;
static const StyleKwEntry STYLE_KWS[] = {
{"linewidth", "lw", 5, KW_SA},
{"linestyle", "ls", 5, KW_SA},
{"pointinterval", "pi", 6, KW_SA},
{"pointnumber", "pn", 6, KW_SA},
{"arrowstyle", "as", 10, KW_SA},
{"dashlength", "dl", 5, KW_SA},
{"linetype", "lt", 8, KW_LT},
{"linecolor", "lc", 5, KW_LC},
{"dashtype", "dt", 5, KW_DT},
{"pointtype", "pt", 6, KW_PT},
{"pointsize", "ps", 6, KW_PS},
{"fillstyle", "fs", 4, KW_FS},
{"fillcolor", "fc", 5, KW_FC},
{"textcolor", "tc", 5, KW_TC},
{NULL, NULL, 0, 0},
};
static int match_style_kw(const char* word, int wlen, const bool* valid_symbols) {
for (int i = 0; STYLE_KWS[i].keyword != NULL; i++) {
const StyleKwEntry* e = &STYLE_KWS[i];
if (!valid_symbols[e->symbol])
continue;
int klen = (int)strlen(e->keyword);
if ((wlen >= e->min_chars && wlen <= klen && strncmp(word, e->keyword, (size_t)wlen) == 0) ||
(e->alt && strcmp(word, e->alt) == 0))
return e->symbol;
}
return -1;
}
typedef struct {
const char* keyword;
int min_chars;
int symbol;
int no_prefix;
} GoptKwEntry;
static const GoptKwEntry GOPT_KWS[] = {
{"on", 2, KW_G_MOD, 0},
{"off", 3, KW_G_MOD, 0},
{"linear", 2, KW_G_MOD, 0},
{"levels", 2, KW_G_ARG, 0},
{"cubicspline", 1, KW_G_ARG, 0},
{"bspline", 1, KW_G_ARG, 0},
{"points", 1, KW_G_ARG, 0},
{"order", 1, KW_G_ARG, 0},
{"origin", 1, KW_G_ARG, 0},
{"auto", 4, KW_G_ARG, 0},
{"discrete", 8, KW_G_ARG, 0},
{"incremental", 2, KW_G_ARG, 0},
{"unsorted", 8, KW_G_ARG, 0},
{"sorted", 6, KW_G_ARG, 0},
{"firstlinetype", 5, KW_G_ARG, 0},
{"defined", 3, KW_G_ARG, 0},
{"errorbars", 5, KW_G_ARG, 0},
{"boxed", 5, KW_G_FLAG, 1},
{"vertical", 1, KW_G_FLAG, 1},
{"horizontal", 1, KW_G_ARG, 0},
{"invert", 3, KW_G_FLAG, 1},
{"user", 1, KW_G_ARG, 0},
{"default", 3, KW_G_ARG, 0},
{"size", 1, KW_G_ARG, 0},
{"front", 2, KW_G_FLAG, 0},
{"back", 2, KW_G_FLAG, 0},
{"noborder", 4, KW_G_FLAG, 0},
{"bdefault", 2, KW_G_MOD, 0},
{"border", 2, KW_G_ARG, 0},
{"cbtics", 6, KW_G_ARG, 0},
{"polar", 2, KW_G_FLAG, 1},
{"layerdefault", 6, KW_G_ARG, 0},
{"spiderplot", 6, KW_G_ARG, 0},
{"degrees", 1, KW_G_ARG, 0},
{"radians", 1, KW_G_ARG, 0},
{"absolute", 1, KW_G_ARG, 0},
{"relative", 1, KW_G_ARG, 0},
{"square", 6, KW_G_FLAG, 1},
{"one", 1, KW_G_ARG, 0},
{"two", 1, KW_G_ARG, 0},
{"radial", 1, KW_G_ARG, 0},
{"classic", 7, KW_G_MOD, 0},
{"podo", 4, KW_G_MOD, 0},
{"base", 2, KW_G_MOD, 0},
{"both", 2, KW_G_MOD, 0},
{"surface", 1, KW_G_MOD, 0},
{"ztics", 5, KW_G_ARG, 0},
{"palette", 3, KW_G_ARG, 0},
{"locale", 6, KW_G_ARG, 0},
{"first", 3, KW_G_COORD, 0},
{"second", 3, KW_G_COORD, 0},
{"graph", 2, KW_G_COORD, 0},
{"screen", 2, KW_G_COORD, 0},
{"character", 4, KW_G_COORD, 0},
{"quiet", 5, KW_G_ARG, 0},
{"numbers", 3, KW_G_ARG, 0},
{"full", 4, KW_G_MOD, 0},
{"trip", 4, KW_G_MOD, 0},
{"offset", 3, KW_G_ARG, 1},
{"trianglepattern", 15, KW_G_ARG, 0},
{"undefined", 5, KW_G_ARG, 1},
{"altdiagonal", 3, KW_G_FLAG, 1},
{"bentover", 4, KW_G_FLAG, 1},
{"defaults", 3, KW_G_MOD, 0},
{"mixed", 3, KW_G_ARG, 0},
{"triangles", 6, KW_G_ARG, 0},
{"insidecolor", 6, KW_G_ARG, 1},
{"overlap", 4, KW_G_ARG, 0},
{"spread", 6, KW_G_ARG, 0},
{"wrap", 4, KW_G_ARG, 0},
{"swarm", 5, KW_G_MOD, 0},
{"cartesian", 9, KW_G_MOD, 0},
{"spherical", 9, KW_G_MOD, 0},
{"cylindrical", 11, KW_G_MOD, 0},
{"doubleclick", 4, KW_G_ARG, 1},
{"zoomcoordinates", 6, KW_G_ARG, 1},
{"zoomfactors", 6, KW_G_ARG, 0},
{"ruler", 5, KW_G_ARG, 1},
{"polardistancedeg", 16, KW_G_ARG, 1},
{"polardistancetan", 16, KW_G_ARG, 1},
{"polardistance", 13, KW_G_ARG, 1},
{"mouseformat", 11, KW_G_ARG, 0},
{"function", 8, KW_G_ARG, 0},
{"labels", 3, KW_G_ARG, 1},
{"zoomjump", 5, KW_G_ARG, 1},
{"verbose", 3, KW_G_ARG, 1},
{"start", 5, KW_G_ARG, 0},
{"interval", 6, KW_G_ARG, 0},
{"onecolor", 8, KW_G_ARG, 0},
{"small", 5, KW_G_MOD, 0},
{"large", 5, KW_G_MOD, 0},
{"fullwidth", 9, KW_G_MOD, 0},
{"x0", 2, KW_G_MOD, 0},
{"x1", 2, KW_G_MOD, 0},
{"y0", 2, KW_G_MOD, 0},
{"y1", 2, KW_G_MOD, 0},
{"z0", 2, KW_G_MOD, 0},
{"counterclockwise", 16, KW_G_MOD, 0},
{"clockwise", 9, KW_G_MOD, 0},
{"ccw", 3, KW_G_MOD, 0},
{"cw", 2, KW_G_MOD, 0},
{"left", 3, KW_G_ARG, 0},
{"right", 3, KW_G_ARG, 0},
{"top", 2, KW_G_ARG, 0},
{"bottom", 3, KW_G_ARG, 0},
{"map", 3, KW_G_ARG, 0},
{"scale", 5, KW_G_ARG, 0},
{"projection", 10, KW_G_ARG, 0},
{"azimuth", 7, KW_G_ARG, 0},
{"equal", 5, KW_G_FLAG, 1},
{"xyz", 3, KW_G_MOD, 0},
{"xy", 2, KW_G_MOD, 0},
{"xz", 2, KW_G_MOD, 0},
{"yz", 2, KW_G_MOD, 0},
{"ratio", 2, KW_G_ARG, 1},
{"width", 5, KW_G_ARG, 0},
{"height", 6, KW_G_ARG, 0},
{"center", 6, KW_G_ARG, 0},
{"behind", 6, KW_G_FLAG, 0},
{"at", 2, KW_G_ARG, 0},
{"colormap", 8, KW_G_ARG, 0},
{"append", 6, KW_G_ARG, 0},
{"new", 3, KW_G_ARG, 0},
{"fix", 3, KW_G_MOD, 0},
{"keepfix", 4, KW_G_MOD, 0},
{"noextend", 5, KW_G_FLAG, 0},
{"numeric", 7, KW_G_MOD, 0},
{"timedate", 8, KW_G_MOD, 0},
{"geographic", 10, KW_G_MOD, 0},
{"cycle", 5, KW_G_ARG, 0},
{"fontscale", 9, KW_G_ARG, 0},
{"gray", 4, KW_G_MOD, 0},
{"color", 5, KW_G_MOD, 0},
{"gamma", 5, KW_G_ARG, 0},
{"gradient", 4, KW_G_ARG, 0},
{"fit2rgbformulae", 7, KW_G_ARG, 0},
{"rgbformulae", 3, KW_G_ARG, 0},
{"functions", 4, KW_G_ARG, 0},
{"cubehelix", 4, KW_G_ARG, 0},
{"cycles", 6, KW_G_ARG, 0},
{"saturation", 10, KW_G_ARG, 0},
{"positive", 3, KW_G_ARG, 0},
{"negative", 3, KW_G_ARG, 0},
{"nops_allcF", 10, KW_G_MOD, 0},
{"ps_allcF", 8, KW_G_MOD, 0},
{"maxcolors", 4, KW_G_ARG, 0},
{"float", 5, KW_G_MOD, 0},
{"hex", 3, KW_G_MOD, 0},
{"range", 5, KW_G_ARG, 0},
{"fraction", 8, KW_G_ARG, 0},
{"outliers", 4, KW_G_FLAG, 1},
{"medianlinewidth", 15, KW_G_ARG, 0},
{"separation", 10, KW_G_ARG, 0},
{"candlesticks", 12, KW_G_MOD, 0},
{"financebars", 11, KW_G_MOD, 0},
{"clustered", 5, KW_G_ARG, 0},
{"gap", 3, KW_G_ARG, 0},
{"rowstacked", 4, KW_G_ARG, 0},
{"columnstacked", 7, KW_G_ARG, 0},
{"nokeyseparators", 5, KW_G_ARG, 0},
{"radius", 3, KW_G_ARG, 0},
{"nodraw", 6, KW_G_MOD, 0},
{"margins", 7, KW_G_ARG, 0},
{"transparent", 5, KW_G_MOD, 0},
{"heads", 5, KW_G_FLAG, 1},
{"head", 4, KW_G_FLAG, 1},
{"backheads", 9, KW_G_FLAG, 0},
{"backhead", 8, KW_G_FLAG, 0},
{"filled", 6, KW_G_FLAG, 1},
{"empty", 5, KW_G_MOD, 0},
{"none", 4, KW_G_MOD, 0},
{"point", 5, KW_G_ARG, 0},
{"rectangle", 3, KW_G_MOD, 0},
{"circle", 4, KW_G_MOD, 0},
{"ellipse", 3, KW_G_MOD, 0},
{"polygon", 4, KW_G_MOD, 0},
{"from", 4, KW_G_ARG, 0},
{"rto", 3, KW_G_ARG, 0},
{"to", 2, KW_G_ARG, 0},
{"arc", 3, KW_G_ARG, 0},
{"angle", 5, KW_G_ARG, 0},
{"wedge", 2, KW_G_FLAG, 1},
{"units", 5, KW_G_ARG, 0},
{"xx", 2, KW_G_MOD, 0},
{"yy", 2, KW_G_MOD, 0},
{"depthorder", 5, KW_G_FLAG, 0},
{"clip", 4, KW_G_FLAG, 1},
{"autotitle", 1, KW_G_ARG, 1},
{"columnheader", 3, KW_G_ARG, 0},
{"box", 3, KW_G_FLAG, 1},
{"opaque", 6, KW_G_FLAG, 1},
{"reverse", 3, KW_G_FLAG, 1},
{"samplen", 7, KW_G_ARG, 0},
{"spacing", 7, KW_G_ARG, 0},
{"keywidth", 4, KW_G_ARG, 0},
{"columns", 7, KW_G_ARG, 0},
{"maxcols", 6, KW_G_ARG, 0},
{"maxrows", 6, KW_G_ARG, 0},
{"inside", 3, KW_G_ARG, 0},
{"outside", 4, KW_G_ARG, 0},
{"Left", 2, KW_G_ARG, 0},
{"Right", 2, KW_G_ARG, 0},
{"fixed", 5, KW_G_MOD, 0},
{"title", 2, KW_G_ARG, 1},
{"lmargin", 2, KW_G_ARG, 0},
{"rmargin", 2, KW_G_ARG, 0},
{"tmargin", 2, KW_G_ARG, 0},
{"bmargin", 2, KW_G_ARG, 0},
{NULL, 0, 0, 0},
};
static int match_gopt_kw(const char* word, int wlen, const bool* valid_symbols) {
for (int pass = 0; pass < 2; pass++) {
const char* w = word;
int len = wlen;
if (pass == 1) { if (wlen < 3 || word[0] != 'n' || word[1] != 'o')
break;
w = word + 2;
len = wlen - 2;
}
for (int i = 0; GOPT_KWS[i].keyword != NULL; i++) {
const GoptKwEntry* e = &GOPT_KWS[i];
if (!valid_symbols[e->symbol])
continue;
if (pass == 1 && !e->no_prefix)
continue;
int klen = (int)strlen(e->keyword);
if (len >= e->min_chars && len <= klen && strncmp(w, e->keyword, (size_t)len) == 0)
return e->symbol;
}
}
return -1;
}
static bool axis_word_suffix(const char* word, int j, int wlen, int kind) {
if (j == wlen) return true; if (kind == 1) {
static const char* sfx[] = {"min", "max", "fix", "fixmin", "fixmax", NULL};
for (int s = 0; sfx[s] != NULL; s++)
if ((int)strlen(sfx[s]) == wlen - j && strncmp(word + j, sfx[s], (size_t)(wlen - j)) == 0)
return true;
return false;
}
int k = 0, i = j;
while (i < wlen && k < 4 && word[i] == "tics"[k]) { i++; k++; }
return i == wlen;
}
static bool match_axis_word(const char* word, int wlen, int kind) {
int i = 0;
if (kind == 0) {
if (wlen >= 2 && word[0] == 'n' && word[1] == 'o') i = 2;
if (i < wlen && word[i] == 'm') i++;
}
static const char* axes2[] = {"x2", "y2", "cb", "vx", "vy", "vz", "xy", NULL};
static const char axes1[] = "xyzrtuv";
for (int a = 0; axes2[a] != NULL; a++) {
if (wlen - i >= 2 && word[i] == axes2[a][0] && word[i + 1] == axes2[a][1]) {
if (axis_word_suffix(word, i + 2, wlen, kind)) return true;
}
}
for (int a = 0; axes1[a] != '\0'; a++) {
if (wlen - i >= 1 && word[i] == axes1[a]) {
if (axis_word_suffix(word, i + 1, wlen, kind)) return true;
}
}
return false;
}
typedef struct {
const char* keyword;
int min_chars;
int symbol;
} CmdKwEntry;
static const CmdKwEntry CMD_KWS[] = {
{"fit", 1, CMD_FIT_KW},
{"plot", 1, CMD_PLOT_KW},
{"splot", 2, CMD_SPLOT_KW},
{"pause", 2, CMD_PAUSE_KW},
{"print", 2, CMD_PRINT_KW},
{"help", 2, CMD_HELP_KW},
{"load", 1, CMD_LOAD_KW},
{"break", 5, KW_CMD_BARE},
{"clear", 2, KW_CMD_BARE},
{"continue", 8, KW_CMD_BARE},
{"pwd", 3, KW_CMD_BARE},
{"replot", 3, KW_CMD_BARE},
{"reread", 6, KW_CMD_BARE},
{"refresh", 3, KW_CMD_BARE},
{"remultiplot", 7, KW_CMD_BARE},
{"raise", 2, KW_CMD_OPTEXPR},
{"lower", 3, KW_CMD_OPTEXPR},
{"vclear", 6, KW_CMD_OPTEXPR},
{"toggle", 6, KW_CMD_OPTEXPR},
{"exit", 2, KW_CMD_EXIT},
{"quit", 1, KW_CMD_EXIT},
{"cd", 2, KW_CMD_EXPR},
{"evaluate", 4, KW_CMD_EXPR},
{NULL, 0, 0},
};
typedef struct {
char word[MAX_WORD_LENGTH];
} Scanner;
static inline void consume(TSLexer* lexer) {
lexer->advance(lexer, false);
}
static inline void skip(TSLexer* lexer) {
lexer->advance(lexer, true);
}
static inline void skip_whitespaces(TSLexer* lexer) {
while (iswspace(lexer->lookahead) || lexer->lookahead == ';' || lexer->lookahead == '\\') skip(lexer);
}
void* tree_sitter_gnuplot_external_scanner_create() {
return calloc(1, sizeof(Scanner));
}
void tree_sitter_gnuplot_external_scanner_destroy(void* payload) {
free(payload);
}
unsigned tree_sitter_gnuplot_external_scanner_serialize(void* payload, char* buffer) {
Scanner* s = (Scanner*)payload;
unsigned len = (unsigned)strlen(s->word);
memcpy(buffer, s->word, len);
return len;
}
void tree_sitter_gnuplot_external_scanner_deserialize(void* payload, const char* buffer, unsigned length) {
Scanner* s = (Scanner*)payload;
if (length > 0) memcpy(s->word, buffer, length);
s->word[length] = '\0';
}
static bool scan_datablock_start(TSLexer* lexer, Scanner* s) {
if (!iswalpha(lexer->lookahead))
return false;
memset(s->word, 0, sizeof(s->word));
int i = 0;
while (iswalpha(lexer->lookahead) && i < MAX_WORD_LENGTH - 1) {
s->word[i++] = lexer->lookahead;
consume(lexer);
}
s->word[i] = '\0';
return true;
}
static bool scan_datablock_end(TSLexer* lexer, Scanner* s) {
if (s->word[0] == '\0')
return false;
int i = 0;
while (iswalpha(lexer->lookahead) && s->word[i] != '\0' && lexer->lookahead == s->word[i]) {
consume(lexer);
i++;
}
if (s->word[i] == '\0' && !iswalpha(lexer->lookahead)) {
return true;
}
return false;
}
static bool is_word_char(int32_t c) {
return iswalnum(c) || c == '_' || c > 127;
}
static int read_word(TSLexer* lexer, char* buf, int cap) {
int len = 0;
while (len < cap - 1 && is_word_char(lexer->lookahead)) {
buf[len++] = (char)lexer->lookahead;
consume(lexer);
}
buf[len] = '\0';
if (len == 0 || is_word_char(lexer->lookahead))
return -1;
return len;
}
static bool is_assignment_context(TSLexer* lexer) {
while (lexer->lookahead == ' ' || lexer->lookahead == '\t') consume(lexer);
if (lexer->lookahead == '=') {
consume(lexer);
return lexer->lookahead != '='; }
if (lexer->lookahead == '(') {
int depth = 1;
consume(lexer);
while (lexer->lookahead != 0 && depth > 0) {
int32_t c = lexer->lookahead;
consume(lexer);
if (c == '(')
depth++;
else if (c == ')')
depth--;
}
while (lexer->lookahead == ' ' || lexer->lookahead == '\t') consume(lexer);
return lexer->lookahead == '=';
}
if (lexer->lookahead == '[') {
consume(lexer);
bool has_colon = false;
int depth = 1;
while (lexer->lookahead != 0 && depth > 0) {
int32_t c = lexer->lookahead;
consume(lexer);
if (c == '[')
depth++;
else if (c == ']')
depth--;
else if (c == ':' && depth == 1)
has_colon = true;
}
if (has_colon)
return false;
while (lexer->lookahead == ' ' || lexer->lookahead == '\t') consume(lexer);
return lexer->lookahead == '=';
}
return false;
}
static bool scan_keywords(TSLexer* lexer, const bool* valid_symbols, bool any_cmd_valid) {
char word[24];
int word_len = read_word(lexer, word, sizeof(word));
if (word_len < 0)
return false;
lexer->mark_end(lexer);
if (any_cmd_valid) {
for (int i = 0; CMD_KWS[i].keyword != NULL; i++) {
const CmdKwEntry* e = &CMD_KWS[i];
if (valid_symbols[e->symbol] && word_len >= e->min_chars &&
word_len <= (int)strlen(e->keyword) && strncmp(word, e->keyword, (size_t)word_len) == 0) {
if (is_assignment_context(lexer))
return false;
lexer->result_symbol = e->symbol;
return true;
}
}
}
if (valid_symbols[KW_PLT_ST] && match_kw_table(word, word_len, PLT_STYLE_KWS)) {
lexer->result_symbol = KW_PLT_ST;
return true;
}
int sym = match_style_kw(word, word_len, valid_symbols);
if (sym >= 0) {
lexer->result_symbol = sym;
return true;
}
if (valid_symbols[KW_G_AXISRANGE] && match_axis_word(word, word_len, 1)) {
lexer->result_symbol = KW_G_AXISRANGE;
return true;
}
if (valid_symbols[KW_G_AXISFLAG] && match_axis_word(word, word_len, 0)) {
lexer->result_symbol = KW_G_AXISFLAG;
return true;
}
sym = match_gopt_kw(word, word_len, valid_symbols);
if (sym >= 0) {
lexer->result_symbol = sym;
return true;
}
return false;
}
bool tree_sitter_gnuplot_external_scanner_scan(void* payload, TSLexer* lexer, const bool* valid_symbols) {
Scanner* s = (Scanner*)payload;
if (valid_symbols[GVAL_SEP]) {
for (;;) {
if (lexer->lookahead == ' ' || lexer->lookahead == '\t' || lexer->lookahead == '\r') {
skip(lexer);
} else if (lexer->lookahead == '\\') {
skip(lexer);
if (lexer->lookahead == '\r') skip(lexer);
if (lexer->lookahead == '\n') skip(lexer);
} else {
break;
}
}
if (lexer->lookahead != '\n' && lexer->lookahead != ';' && lexer->lookahead != 0 &&
lexer->lookahead != '#') {
lexer->mark_end(lexer); if (is_word_char(lexer->lookahead)) {
char peek[24];
int plen = 0;
while (plen < (int)sizeof(peek) - 1 && is_word_char(lexer->lookahead)) {
peek[plen++] = (char)lexer->lookahead;
consume(lexer);
}
peek[plen] = '\0';
bool word_ended = !is_word_char(lexer->lookahead);
{
int32_t c = lexer->lookahead;
while (c == ' ' || c == '\t') { consume(lexer); c = lexer->lookahead; }
if (c == ',' || c == '+' || c == '-' || c == '*' || c == '/' ||
c == '%' || c == '^' || c == '(' || c == '[' || c == '.' ||
c == '=' || c == '<' || c == '>' || c == '&' || c == '|' ||
c == '?' || c == ':') {
lexer->result_symbol = GVAL_SEP;
return true;
}
}
if (word_ended) {
int s = match_style_kw(peek, plen, valid_symbols);
if (s < 0 && valid_symbols[KW_G_AXISRANGE] && match_axis_word(peek, plen, 1))
s = KW_G_AXISRANGE;
if (s < 0 && valid_symbols[KW_G_AXISFLAG] && match_axis_word(peek, plen, 0))
s = KW_G_AXISFLAG;
if (s < 0)
s = match_gopt_kw(peek, plen, valid_symbols);
if (s >= 0) {
lexer->mark_end(lexer); lexer->result_symbol = s;
return true;
}
}
lexer->result_symbol = GVAL_SEP;
return true;
}
{
int32_t c = lexer->lookahead;
if ((c >= '0' && c <= '9') || c == '.' || c == '"' || c == '\'' ||
c == '(' || c == '-' || c == '+' || c == '~' || c == '!' ||
c == '$' || c == '@') {
lexer->result_symbol = GVAL_SEP;
return true;
}
}
}
}
skip_whitespaces(lexer);
if (valid_symbols[DATABLOCK_END] && scan_datablock_end(lexer, s)) {
lexer->result_symbol = DATABLOCK_END;
return true;
}
bool any_cmd_valid = valid_symbols[CMD_FIT_KW] || valid_symbols[CMD_PLOT_KW] || valid_symbols[CMD_SPLOT_KW] ||
valid_symbols[CMD_PAUSE_KW] || valid_symbols[CMD_PRINT_KW] || valid_symbols[CMD_HELP_KW] ||
valid_symbols[CMD_LOAD_KW] || valid_symbols[KW_CMD_BARE] || valid_symbols[KW_CMD_OPTEXPR] ||
valid_symbols[KW_CMD_EXIT] || valid_symbols[KW_CMD_EXPR];
bool any_style_valid =
valid_symbols[KW_PLT_ST] || valid_symbols[KW_SA] || valid_symbols[KW_LT] ||
valid_symbols[KW_LC] || valid_symbols[KW_DT] || valid_symbols[KW_PT] ||
valid_symbols[KW_PS] || valid_symbols[KW_FS] || valid_symbols[KW_FC] ||
valid_symbols[KW_TC];
bool any_gopt_valid =
valid_symbols[KW_G_ARG] || valid_symbols[KW_G_FLAG] || valid_symbols[KW_G_MOD] ||
valid_symbols[KW_G_COORD] || valid_symbols[KW_G_AXISFLAG] ||
valid_symbols[KW_G_AXISRANGE];
if ((any_cmd_valid || any_style_valid || any_gopt_valid) &&
scan_keywords(lexer, valid_symbols, any_cmd_valid)) {
return true;
}
if (valid_symbols[DATABLOCK_START] && scan_datablock_start(lexer, s)) {
lexer->result_symbol = DATABLOCK_START;
return true;
}
return false;
}