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 <ctype.h>
26 #include <limits.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
32 typedef struct {
33 union {
34 char *str;
35 int num;
36 };
37 int lineno;
38 int colno;
39 } yystype;
40 #define YYSTYPE yystype
42 static char *current_style;
43 static const char *path;
45 FILE *yyfp;
47 int parse_errors = 0;
49 static void yyerror(const char *, ...);
50 static int yylex(void);
52 %}
54 %token TSET
55 %token TSTYLE TPRFX TCONT TBG TFG TATTR TBOLD TUNDERLINE
56 %token TBIND TUNBIND
58 %token <str> TSTRING
59 %token <num> TNUMBER
61 %%
63 grammar : /* empty */
64 | grammar '\n'
65 | grammar rule '\n'
66 | error '\n'
67 ;
69 rule : set
70 | style { free(current_style); current_style = NULL; }
71 | bind
72 | unbind
73 ;
75 set : TSET TSTRING '=' TSTRING { printf("set %s = \"%s\"\n", $2, $4); }
76 | TSET TSTRING '=' TNUMBER { printf("set %s = %d\n", $2, $4); }
77 ;
79 style : TSTYLE TSTRING { current_style = $2; } styleopt
80 | TSTYLE TSTRING { current_style = $2; } '{' styleopts '}'
81 ;
83 styleopts : /* empty */
84 | styleopts '\n'
85 | styleopts styleopt '\n'
86 ;
88 styleopt : TPRFX TSTRING { setprfx(0, $2); }
89 | TCONT TSTRING { setprfx(1, $2); }
90 | TBG TSTRING { printf("style background setted to \"%s\"\n", $2); }
91 | TFG TSTRING { printf("style foreground setted to \"%s\"\n", $2); }
92 | TATTR TBOLD { printf("style attr setted to bold\n"); }
93 | TATTR TUNDERLINE { printf("style attr setted to underline\n"); }
94 ;
96 bind : TBIND TSTRING TSTRING TSTRING { printf("TODO: bind %s %s %s\n", $2, $3, $4); }
97 ;
99 unbind : TUNBIND TSTRING TSTRING { printf("TODO: unbind %s %s\n", $2, $3); }
102 %%
104 void
105 yyerror(const char *fmt, ...)
107 va_list va;
109 fprintf(stderr, "%s:%d ", path, yylval.lineno+1);
110 va_start(va, fmt);
111 vfprintf(stderr, fmt, va);
112 va_end(va);
113 fprintf(stderr, "\n");
114 parse_errors++;
117 static struct keyword {
118 const char *word;
119 int token;
120 } keywords[] = {
121 { "set", TSET },
122 { "style", TSTYLE },
123 { "prefix", TPRFX },
124 { "cont", TCONT },
125 { "background", TBG },
126 { "foreground", TFG },
127 { "attr", TATTR },
128 { "bold", TBOLD },
129 { "underline", TUNDERLINE },
130 { "bind", TBIND },
131 { "unbind", TUNBIND },
132 };
134 int
135 yylex(void)
137 char buf[1024], *ebuf, *p, *str;
138 const char *errstr;
139 int c, quotes = 0, escape = 0, qpos = -1, nonkw = 0;
140 size_t i;
142 p = buf;
143 ebuf = buf + sizeof(buf);
145 repeat:
146 /* skip whitespace first */
147 for (c = getc(yyfp); c == ' ' || c == '\t' || c == '\f'; c = getc(yyfp))
148 yylval.colno++;
150 /* check for special one-character constructions */
151 switch (c) {
152 case '\n':
153 yylval.colno = 0;
154 yylval.lineno++;
155 /* fallthrough */
156 case '{':
157 case '}':
158 case '=':
159 return c;
160 case '#':
161 /* skip comments; NUL is allowed; no continuation */
162 while ((c = getc(yyfp)) != '\n')
163 if (c == EOF)
164 goto eof;
165 yylval.colno = 0;
166 yylval.lineno++;
167 return c;
168 case EOF:
169 goto eof;
172 /* parsing next word */
173 for (;; c = getc(yyfp), yylval.colno++) {
174 switch (c) {
175 case '\0':
176 yyerror("unallowed character NULL in column %d",
177 yylval.colno+1);
178 escape = 0;
179 continue;
180 case '\\':
181 escape = !escape;
182 if (escape)
183 continue;
184 break;
185 case '\n':
186 if (quotes)
187 yyerror("unterminated quotes in column %d",
188 yylval.colno+1);
189 if (escape) {
190 nonkw = 1;
191 escape = 0;
192 yylval.colno = 0;
193 yylval.lineno++;
195 goto eow;
196 case EOF:
197 if (escape)
198 yyerror("unterminated escape in column %d",
199 yylval.colno);
200 if (quotes)
201 yyerror("unterminated quotes in column %d",
202 qpos + 1);
203 goto eow;
204 case '{':
205 case '}':
206 case '=':
207 case '#':
208 case ' ':
209 case '\t':
210 if (!escape && !quotes)
211 goto eow;
212 break;
213 case '"':
214 if (!escape) {
215 quotes = !quotes;
216 if (quotes) {
217 nonkw = 1;
218 qpos = yylval.colno;
220 continue;
223 *p++ = c;
224 if (p == ebuf) {
225 yyerror("line too long");
226 p = buf;
228 escape = 0;
231 eow:
232 *p = 0;
233 if (c != EOF)
234 ungetc(c, yyfp);
235 if (p == buf) {
236 /*
237 * There could be a number of reason for empty buffer,
238 * and we handle all of them here, to avoid cluttering
239 * the main loop.
240 */
241 if (c == EOF)
242 goto eof;
243 else if (qpos == -1) /* accept, e.g., empty args: cmd foo args "" */
244 goto repeat;
246 if (!nonkw) {
247 for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
248 if (strcmp(buf, keywords[i].word) == 0)
249 return keywords[i].token;
252 c = *buf;
253 if (!nonkw && (c == '-' || isdigit(c))) {
254 yylval.num = strtonum(buf, INT_MIN, INT_MAX, &errstr);
255 if (errstr != NULL)
256 yyerror("number is %s: %s", errstr, buf);
257 return TNUMBER;
259 if ((str = strdup(buf)) == NULL)
260 err(1, "%s", __func__);
261 yylval.str = str;
262 return TSTRING;
264 eof:
265 if (ferror(yyfp))
266 yyerror("input error reading config");
267 return 0;
270 static void
271 setprfx(int cont, const char *name)
273 if (current_style == NULL) {
274 warnx("current_style = NULL!");
275 abort();
278 if (!config_setprfx(current_style, cont, name))
279 yyerror("invalid style %s", name);
282 void
283 parseconfig(const char *filename, int fonf)
285 if ((yyfp = fopen(filename, "r")) == NULL) {
286 if (fonf)
287 err(1, "%s", filename);
288 return;
291 path = filename;
292 yyparse();
293 fclose(yyfp);
294 if (parse_errors)
295 exit(1);