Blob


1 /*
2 * Much of the design is taken from doas (parse.y rev 1.29)
3 *
4 * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
5 * Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 *
19 */
21 %{
23 #include "telescope.h"
25 #include <assert.h>
26 #include <ctype.h>
27 #include <limits.h>
28 #include <ncurses.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
34 typedef struct {
35 union {
36 char *str;
37 int num;
38 };
39 int lineno;
40 int colno;
41 } yystype;
42 #define YYSTYPE yystype
44 static char *current_style;
45 static int current_bg, current_fg;
47 static const char *path;
49 FILE *yyfp;
51 int parse_errors = 0;
53 static void yyerror(const char *, ...);
54 static int yylex(void);
55 static void setprfx(int, const char *);
56 static void setvari(char *, int);
57 static void setvars(char *, char *);
58 static int colorname(const char *);
59 static void setcolor(int, int, const char *);
61 %}
63 %token TSET
64 %token TSTYLE TPRFX TCONT TBG TFG TATTR TBOLD TUNDERLINE
65 %token TBIND TUNBIND
67 %token <str> TSTRING
68 %token <num> TNUMBER
70 %%
72 grammar : /* empty */
73 | grammar '\n'
74 | grammar rule '\n'
75 | error '\n'
76 ;
78 rule : set
79 | style {
80 free(current_style);
81 current_style = NULL;
82 }
83 | bind
84 | unbind
85 ;
87 set : TSET TSTRING '=' TSTRING { setvars($2, $4); }
88 | TSET TSTRING '=' TNUMBER { setvari($2, $4); }
89 ;
91 style : TSTYLE TSTRING { current_style = $2; } stylespec ;
92 stylespec : styleopt | '{' styleopts '}' ;
94 styleopts : /* empty */
95 | styleopts '\n'
96 | styleopts styleopt '\n'
97 ;
99 styleopt : TPRFX TSTRING { setprfx(0, $2); }
100 | TCONT TSTRING { setprfx(1, $2); }
101 | TBG TSTRING {
102 setcolor(0, 1, $2);
103 setcolor(1, 1, $2);
104 free($2);
106 | TFG TSTRING {
107 setcolor(0, 0, $2);
108 setcolor(1, 0, $2);
109 free($2);
111 | TATTR TBOLD { printf("style attr setted to bold\n"); }
112 | TATTR TUNDERLINE { printf("style attr setted to underline\n"); }
115 bind : TBIND TSTRING TSTRING TSTRING { printf("TODO: bind %s %s %s\n", $2, $3, $4); }
118 unbind : TUNBIND TSTRING TSTRING { printf("TODO: unbind %s %s\n", $2, $3); }
121 %%
123 void
124 yyerror(const char *fmt, ...)
126 va_list va;
128 fprintf(stderr, "%s:%d ", path, yylval.lineno+1);
129 va_start(va, fmt);
130 vfprintf(stderr, fmt, va);
131 va_end(va);
132 fprintf(stderr, "\n");
133 parse_errors++;
136 static struct keyword {
137 const char *word;
138 int token;
139 } keywords[] = {
140 { "set", TSET },
141 { "style", TSTYLE },
142 { "prefix", TPRFX },
143 { "cont", TCONT },
144 { "bg", TBG },
145 { "fg", TFG },
146 { "attr", TATTR },
147 { "bold", TBOLD },
148 { "underline", TUNDERLINE },
149 { "bind", TBIND },
150 { "unbind", TUNBIND },
151 };
153 int
154 yylex(void)
156 char buf[1024], *ebuf, *p, *str;
157 const char *errstr;
158 int c, quotes = 0, escape = 0, qpos = -1, nonkw = 0;
159 size_t i;
161 p = buf;
162 ebuf = buf + sizeof(buf);
164 repeat:
165 /* skip whitespace first */
166 for (c = getc(yyfp); c == ' ' || c == '\t' || c == '\f'; c = getc(yyfp))
167 yylval.colno++;
169 /* check for special one-character constructions */
170 switch (c) {
171 case '\n':
172 yylval.colno = 0;
173 yylval.lineno++;
174 /* fallthrough */
175 case '{':
176 case '}':
177 case '=':
178 return c;
179 case '#':
180 /* skip comments; NUL is allowed; no continuation */
181 while ((c = getc(yyfp)) != '\n')
182 if (c == EOF)
183 goto eof;
184 yylval.colno = 0;
185 yylval.lineno++;
186 return c;
187 case EOF:
188 goto eof;
191 /* parsing next word */
192 for (;; c = getc(yyfp), yylval.colno++) {
193 switch (c) {
194 case '\0':
195 yyerror("unallowed character NULL in column %d",
196 yylval.colno+1);
197 escape = 0;
198 continue;
199 case '\\':
200 escape = !escape;
201 if (escape)
202 continue;
203 break;
204 case '\n':
205 if (quotes)
206 yyerror("unterminated quotes in column %d",
207 yylval.colno+1);
208 if (escape) {
209 nonkw = 1;
210 escape = 0;
211 yylval.colno = 0;
212 yylval.lineno++;
214 goto eow;
215 case EOF:
216 if (escape)
217 yyerror("unterminated escape in column %d",
218 yylval.colno);
219 if (quotes)
220 yyerror("unterminated quotes in column %d",
221 qpos + 1);
222 goto eow;
223 case '{':
224 case '}':
225 case '=':
226 case '#':
227 case ' ':
228 case '\t':
229 if (!escape && !quotes)
230 goto eow;
231 break;
232 case '"':
233 if (!escape) {
234 quotes = !quotes;
235 if (quotes) {
236 nonkw = 1;
237 qpos = yylval.colno;
239 continue;
242 *p++ = c;
243 if (p == ebuf) {
244 yyerror("line too long");
245 p = buf;
247 escape = 0;
250 eow:
251 *p = 0;
252 if (c != EOF)
253 ungetc(c, yyfp);
254 if (p == buf) {
255 /*
256 * There could be a number of reason for empty buffer,
257 * and we handle all of them here, to avoid cluttering
258 * the main loop.
259 */
260 if (c == EOF)
261 goto eof;
262 else if (qpos == -1) /* accept, e.g., empty args: cmd foo args "" */
263 goto repeat;
265 if (!nonkw) {
266 for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
267 if (strcmp(buf, keywords[i].word) == 0)
268 return keywords[i].token;
271 c = *buf;
272 if (!nonkw && (c == '-' || isdigit(c))) {
273 yylval.num = strtonum(buf, INT_MIN, INT_MAX, &errstr);
274 if (errstr != NULL)
275 yyerror("number is %s: %s", errstr, buf);
276 return TNUMBER;
278 if ((str = strdup(buf)) == NULL)
279 err(1, "%s", __func__);
280 yylval.str = str;
281 return TSTRING;
283 eof:
284 if (ferror(yyfp))
285 yyerror("input error reading config");
286 return 0;
289 static void
290 setprfx(int cont, const char *name)
292 assert(current_style != NULL);
294 if (!config_setprfx(current_style, cont, name))
295 yyerror("invalid style %s", name);
298 static void
299 setvari(char *var, int val)
301 if (!config_setvari(var, val))
302 yyerror("invalid variable or value: %s = %d",
303 var, val);
305 free(var);
308 static void
309 setvars(char *var, char *val)
311 if (!config_setvars(var, val))
312 yyerror("invalid variable or value: %s = \"%s\"",
313 var, val);
315 free(var);
318 static int
319 colorname(const char *name)
321 struct {
322 const char *name;
323 short val;
324 } *i, colors[] = {
325 { "default", 0 },
326 { "black", COLOR_BLACK },
327 { "red", COLOR_RED },
328 { "green", COLOR_GREEN },
329 { "yellow", COLOR_YELLOW },
330 { "blue", COLOR_BLUE },
331 { "magenta", COLOR_MAGENTA },
332 { "cyan", COLOR_CYAN },
333 { "white", COLOR_WHITE },
334 { NULL, 0 },
335 };
337 for (i = colors; i->name != NULL; ++i) {
338 if (!strcmp(i->name, name))
339 return i->val;
342 yyerror("unknown color name \"%s\"", name);
343 return -1;
346 void
347 setcolor(int prfx, int bg, const char *color)
349 int c;
351 assert(current_style != NULL);
353 if ((c = colorname(color)) == -1)
354 return;
356 if (!config_setcolor(current_style, prfx, bg, c))
357 yyerror("can't set color \"%s\" for %s", color,
358 current_style);
361 void
362 parseconfig(const char *filename, int fonf)
364 /*
365 char c;
366 printf("proceed?\n");
367 read(0, &c, 1);
368 */
370 if ((yyfp = fopen(filename, "r")) == NULL) {
371 if (fonf)
372 err(1, "%s", filename);
373 return;
376 path = filename;
377 yyparse();
378 fclose(yyfp);
379 if (parse_errors)
380 exit(1);