commit 69bdd906cbdaaab353fb7f2d20fd70c31c19d31d from: Omar Polo date: Tue Jun 15 13:54:32 2021 UTC first revision of a parser for the configuration file There is only the code to do the parsing, not actually applying them. The accepted syntax is as follows: # `set' to, hum, set options set max-redirect = 5 set new-tab-url = "about:blank" # change the styling style tabline foreground white style tabline background black style line.title2 { prefix "## " cont " " attr bold } # keybindings management bind global-map "1 G" beginning-of-buffer bind minibuffer-map "C-c" minibuffer-abort unbind minibuffer-map "C-g" commit - 89fd2bea5a51ccb48581f8c7acf5788e35ead60a commit + 69bdd906cbdaaab353fb7f2d20fd70c31c19d31d blob - 7fd8be6ca2455868777513733d2496ac021f1e17 blob + 197094d148d98d5113efad3d6bd01f9a877d85fc --- configure.ac +++ configure.ac @@ -3,6 +3,7 @@ AC_CONFIG_LIBOBJ_DIR(compat) AM_INIT_AUTOMAKE([-Wall foreign subdir-objects]) AC_PROG_CC_C99 AC_USE_SYSTEM_EXTENSIONS +AC_PROG_YACC PKG_PROG_PKG_CONFIG blob - /dev/null blob + e03c44868486748ab561d70e313109cd0f80d495 (mode 644) --- /dev/null +++ parse.y @@ -0,0 +1,279 @@ +/* + * Much of the design is taken from doas (parse.y rev 1.29) + * + * Copyright (c) 2021 Omar Polo + * Copyright (c) 2015 Ted Unangst + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +%{ + +#include "telescope.h" + +#include +#include +#include +#include +#include +#include + +typedef struct { + union { + char *str; + int num; + }; + int lineno; + int colno; +} yystype; +#define YYSTYPE yystype + +static const char *path; + +FILE *yyfp; + +int parse_errors = 0; + +static void yyerror(const char *, ...); +static int yylex(void); + +%} + +%token TSET +%token TSTYLE TPRFX TCONT TBG TFG TATTR TBOLD TUNDERLINE +%token TBIND TUNBIND + +%token TSTRING +%token TNUMBER + +%% + +grammar : /* empty */ + | grammar '\n' + | grammar rule '\n' + | error '\n' + ; + +rule : set | style | bind | unbind ; + +set : TSET TSTRING '=' TSTRING { printf("set %s = \"%s\"\n", $2, $4); } + | TSET TSTRING '=' TNUMBER { printf("set %s = %d\n", $2, $4); } + ; + +style : TSTYLE TSTRING { printf("(%s) ", $2); } styleopt + | TSTYLE TSTRING { printf("[%s]\n", $2); } '{' styleopts '}' + ; + +styleopts : /* empty */ + | styleopts '\n' + | styleopts styleopt '\n' + ; + +styleopt : TPRFX TSTRING { printf("style prefix setted to \"%s\"\n", $2); } + | TCONT TSTRING { printf("style cont setted to \"%s\"\n", $2); } + | TBG TSTRING { printf("style background setted to \"%s\"\n", $2); } + | TFG TSTRING { printf("style foreground setted to \"%s\"\n", $2); } + | TATTR TBOLD { printf("style attr setted to bold\n"); } + | TATTR TUNDERLINE { printf("style attr setted to underline\n"); } + ; + +bind : TBIND TSTRING TSTRING TSTRING { printf("TODO: bind %s %s %s\n", $2, $3, $4); } + ; + +unbind : TUNBIND TSTRING TSTRING { printf("TODO: unbind %s %s\n", $2, $3); } + ; + +%% + +void +yyerror(const char *fmt, ...) +{ + va_list va; + + fprintf(stderr, "%s:%d ", path, yylval.lineno+1); + va_start(va, fmt); + vfprintf(stderr, fmt, va); + va_end(va); + fprintf(stderr, "\n"); + parse_errors++; +} + +static struct keyword { + const char *word; + int token; +} keywords[] = { + { "set", TSET }, + { "style", TSTYLE }, + { "prefix", TPRFX }, + { "cont", TCONT }, + { "background", TBG }, + { "foreground", TFG }, + { "attr", TATTR }, + { "bold", TBOLD }, + { "underline", TUNDERLINE }, + { "bind", TBIND }, + { "unbind", TUNBIND }, +}; + +int +yylex(void) +{ + char buf[1024], *ebuf, *p, *str; + const char *errstr; + int c, quotes = 0, escape = 0, qpos = -1, nonkw = 0; + size_t i; + + p = buf; + ebuf = buf + sizeof(buf); + +repeat: + /* skip whitespace first */ + for (c = getc(yyfp); c == ' ' || c == '\t' || c == '\f'; c = getc(yyfp)) + yylval.colno++; + + /* check for special one-character constructions */ + switch (c) { + case '\n': + yylval.colno = 0; + yylval.lineno++; + /* fallthrough */ + case '{': + case '}': + case '=': + return c; + case '#': + /* skip comments; NUL is allowed; no continuation */ + while ((c = getc(yyfp)) != '\n') + if (c == EOF) + goto eof; + yylval.colno = 0; + yylval.lineno++; + return c; + case EOF: + goto eof; + } + + /* parsing next word */ + for (;; c = getc(yyfp), yylval.colno++) { + switch (c) { + case '\0': + yyerror("unallowed character NULL in column %d", + yylval.colno+1); + escape = 0; + continue; + case '\\': + escape = !escape; + if (escape) + continue; + break; + case '\n': + if (quotes) + yyerror("unterminated quotes in column %d", + yylval.colno+1); + if (escape) { + nonkw = 1; + escape = 0; + yylval.colno = 0; + yylval.lineno++; + } + goto eow; + case EOF: + if (escape) + yyerror("unterminated escape in column %d", + yylval.colno); + if (quotes) + yyerror("unterminated quotes in column %d", + qpos + 1); + goto eow; + case '{': + case '}': + case '=': + case '#': + case ' ': + case '\t': + if (!escape && !quotes) + goto eow; + break; + case '"': + if (!escape) { + quotes = !quotes; + if (quotes) { + nonkw = 1; + qpos = yylval.colno; + } + continue; + } + } + *p++ = c; + if (p == ebuf) { + yyerror("line too long"); + p = buf; + } + escape = 0; + } + +eow: + *p = 0; + if (c != EOF) + ungetc(c, yyfp); + if (p == buf) { + /* + * There could be a number of reason for empty buffer, + * and we handle all of them here, to avoid cluttering + * the main loop. + */ + if (c == EOF) + goto eof; + else if (qpos == -1) /* accept, e.g., empty args: cmd foo args "" */ + goto repeat; + } + if (!nonkw) { + for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) { + if (strcmp(buf, keywords[i].word) == 0) + return keywords[i].token; + } + } + c = *buf; + if (!nonkw && (c == '-' || isdigit(c))) { + yylval.num = strtonum(buf, INT_MIN, INT_MAX, &errstr); + if (errstr != NULL) + yyerror("number is %s: %s", errstr, buf); + return TNUMBER; + } + if ((str = strdup(buf)) == NULL) + err(1, "%s", __func__); + yylval.str = str; + return TSTRING; + +eof: + if (ferror(yyfp)) + yyerror("input error reading config"); + return 0; +} + +void +parseconfig(const char *filename, int fonf) +{ + if ((yyfp = fopen(filename, "r")) == NULL) { + if (fonf) + err(1, "%s", filename); + return; + } + + path = filename; + yyparse(); + fclose(yyfp); + if (parse_errors) + exit(1); +}