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);
51 static void setprfx(int, const char *);
53 %}
55 %token TSET
56 %token TSTYLE TPRFX TCONT TBG TFG TATTR TBOLD TUNDERLINE
57 %token TBIND TUNBIND
59 %token <str> TSTRING
60 %token <num> TNUMBER
62 %%
64 grammar : /* empty */
65 | grammar '\n'
66 | grammar rule '\n'
67 | error '\n'
68 ;
70 rule : set
71 | style { free(current_style); current_style = NULL; }
72 | bind
73 | unbind
74 ;
76 set : TSET TSTRING '=' TSTRING { printf("set %s = \"%s\"\n", $2, $4); }
77 | TSET TSTRING '=' TNUMBER { printf("set %s = %d\n", $2, $4); }
78 ;
80 style : TSTYLE TSTRING { current_style = $2; } styleopt
81 | TSTYLE TSTRING { current_style = $2; } '{' styleopts '}'
82 ;
84 styleopts : /* empty */
85 | styleopts '\n'
86 | styleopts styleopt '\n'
87 ;
89 styleopt : TPRFX TSTRING { setprfx(0, $2); }
90 | TCONT TSTRING { setprfx(1, $2); }
91 | TBG TSTRING { printf("style background setted to \"%s\"\n", $2); }
92 | TFG TSTRING { printf("style foreground setted to \"%s\"\n", $2); }
93 | TATTR TBOLD { printf("style attr setted to bold\n"); }
94 | TATTR TUNDERLINE { printf("style attr setted to underline\n"); }
95 ;
97 bind : TBIND TSTRING TSTRING TSTRING { printf("TODO: bind %s %s %s\n", $2, $3, $4); }
98 ;
100 unbind : TUNBIND TSTRING TSTRING { printf("TODO: unbind %s %s\n", $2, $3); }
103 %%
105 void
106 yyerror(const char *fmt, ...)
108 va_list va;
110 fprintf(stderr, "%s:%d ", path, yylval.lineno+1);
111 va_start(va, fmt);
112 vfprintf(stderr, fmt, va);
113 va_end(va);
114 fprintf(stderr, "\n");
115 parse_errors++;
118 static struct keyword {
119 const char *word;
120 int token;
121 } keywords[] = {
122 { "set", TSET },
123 { "style", TSTYLE },
124 { "prefix", TPRFX },
125 { "cont", TCONT },
126 { "background", TBG },
127 { "foreground", TFG },
128 { "attr", TATTR },
129 { "bold", TBOLD },
130 { "underline", TUNDERLINE },
131 { "bind", TBIND },
132 { "unbind", TUNBIND },
133 };
135 int
136 yylex(void)
138 char buf[1024], *ebuf, *p, *str;
139 const char *errstr;
140 int c, quotes = 0, escape = 0, qpos = -1, nonkw = 0;
141 size_t i;
143 p = buf;
144 ebuf = buf + sizeof(buf);
146 repeat:
147 /* skip whitespace first */
148 for (c = getc(yyfp); c == ' ' || c == '\t' || c == '\f'; c = getc(yyfp))
149 yylval.colno++;
151 /* check for special one-character constructions */
152 switch (c) {
153 case '\n':
154 yylval.colno = 0;
155 yylval.lineno++;
156 /* fallthrough */
157 case '{':
158 case '}':
159 case '=':
160 return c;
161 case '#':
162 /* skip comments; NUL is allowed; no continuation */
163 while ((c = getc(yyfp)) != '\n')
164 if (c == EOF)
165 goto eof;
166 yylval.colno = 0;
167 yylval.lineno++;
168 return c;
169 case EOF:
170 goto eof;
173 /* parsing next word */
174 for (;; c = getc(yyfp), yylval.colno++) {
175 switch (c) {
176 case '\0':
177 yyerror("unallowed character NULL in column %d",
178 yylval.colno+1);
179 escape = 0;
180 continue;
181 case '\\':
182 escape = !escape;
183 if (escape)
184 continue;
185 break;
186 case '\n':
187 if (quotes)
188 yyerror("unterminated quotes in column %d",
189 yylval.colno+1);
190 if (escape) {
191 nonkw = 1;
192 escape = 0;
193 yylval.colno = 0;
194 yylval.lineno++;
196 goto eow;
197 case EOF:
198 if (escape)
199 yyerror("unterminated escape in column %d",
200 yylval.colno);
201 if (quotes)
202 yyerror("unterminated quotes in column %d",
203 qpos + 1);
204 goto eow;
205 case '{':
206 case '}':
207 case '=':
208 case '#':
209 case ' ':
210 case '\t':
211 if (!escape && !quotes)
212 goto eow;
213 break;
214 case '"':
215 if (!escape) {
216 quotes = !quotes;
217 if (quotes) {
218 nonkw = 1;
219 qpos = yylval.colno;
221 continue;
224 *p++ = c;
225 if (p == ebuf) {
226 yyerror("line too long");
227 p = buf;
229 escape = 0;
232 eow:
233 *p = 0;
234 if (c != EOF)
235 ungetc(c, yyfp);
236 if (p == buf) {
237 /*
238 * There could be a number of reason for empty buffer,
239 * and we handle all of them here, to avoid cluttering
240 * the main loop.
241 */
242 if (c == EOF)
243 goto eof;
244 else if (qpos == -1) /* accept, e.g., empty args: cmd foo args "" */
245 goto repeat;
247 if (!nonkw) {
248 for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
249 if (strcmp(buf, keywords[i].word) == 0)
250 return keywords[i].token;
253 c = *buf;
254 if (!nonkw && (c == '-' || isdigit(c))) {
255 yylval.num = strtonum(buf, INT_MIN, INT_MAX, &errstr);
256 if (errstr != NULL)
257 yyerror("number is %s: %s", errstr, buf);
258 return TNUMBER;
260 if ((str = strdup(buf)) == NULL)
261 err(1, "%s", __func__);
262 yylval.str = str;
263 return TSTRING;
265 eof:
266 if (ferror(yyfp))
267 yyerror("input error reading config");
268 return 0;
271 static void
272 setprfx(int cont, const char *name)
274 if (current_style == NULL) {
275 warnx("current_style = NULL!");
276 abort();
279 if (!config_setprfx(current_style, cont, name))
280 yyerror("invalid style %s", name);
283 void
284 parseconfig(const char *filename, int fonf)
286 if ((yyfp = fopen(filename, "r")) == NULL) {
287 if (fonf)
288 err(1, "%s", filename);
289 return;
292 path = filename;
293 yyparse();
294 fclose(yyfp);
295 if (parse_errors)
296 exit(1);