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 const char *path;
44 FILE *yyfp;
46 int parse_errors = 0;
48 static void yyerror(const char *, ...);
49 static int yylex(void);
51 %}
53 %token TSET
54 %token TSTYLE TPRFX TCONT TBG TFG TATTR TBOLD TUNDERLINE
55 %token TBIND TUNBIND
57 %token <str> TSTRING
58 %token <num> TNUMBER
60 %%
62 grammar : /* empty */
63 | grammar '\n'
64 | grammar rule '\n'
65 | error '\n'
66 ;
68 rule : set | style | bind | unbind ;
70 set : TSET TSTRING '=' TSTRING { printf("set %s = \"%s\"\n", $2, $4); }
71 | TSET TSTRING '=' TNUMBER { printf("set %s = %d\n", $2, $4); }
72 ;
74 style : TSTYLE TSTRING { printf("(%s) ", $2); } styleopt
75 | TSTYLE TSTRING { printf("[%s]\n", $2); } '{' styleopts '}'
76 ;
78 styleopts : /* empty */
79 | styleopts '\n'
80 | styleopts styleopt '\n'
81 ;
83 styleopt : TPRFX TSTRING { printf("style prefix setted to \"%s\"\n", $2); }
84 | TCONT TSTRING { printf("style cont setted to \"%s\"\n", $2); }
85 | TBG TSTRING { printf("style background setted to \"%s\"\n", $2); }
86 | TFG TSTRING { printf("style foreground setted to \"%s\"\n", $2); }
87 | TATTR TBOLD { printf("style attr setted to bold\n"); }
88 | TATTR TUNDERLINE { printf("style attr setted to underline\n"); }
89 ;
91 bind : TBIND TSTRING TSTRING TSTRING { printf("TODO: bind %s %s %s\n", $2, $3, $4); }
92 ;
94 unbind : TUNBIND TSTRING TSTRING { printf("TODO: unbind %s %s\n", $2, $3); }
95 ;
97 %%
99 void
100 yyerror(const char *fmt, ...)
102 va_list va;
104 fprintf(stderr, "%s:%d ", path, yylval.lineno+1);
105 va_start(va, fmt);
106 vfprintf(stderr, fmt, va);
107 va_end(va);
108 fprintf(stderr, "\n");
109 parse_errors++;
112 static struct keyword {
113 const char *word;
114 int token;
115 } keywords[] = {
116 { "set", TSET },
117 { "style", TSTYLE },
118 { "prefix", TPRFX },
119 { "cont", TCONT },
120 { "background", TBG },
121 { "foreground", TFG },
122 { "attr", TATTR },
123 { "bold", TBOLD },
124 { "underline", TUNDERLINE },
125 { "bind", TBIND },
126 { "unbind", TUNBIND },
127 };
129 int
130 yylex(void)
132 char buf[1024], *ebuf, *p, *str;
133 const char *errstr;
134 int c, quotes = 0, escape = 0, qpos = -1, nonkw = 0;
135 size_t i;
137 p = buf;
138 ebuf = buf + sizeof(buf);
140 repeat:
141 /* skip whitespace first */
142 for (c = getc(yyfp); c == ' ' || c == '\t' || c == '\f'; c = getc(yyfp))
143 yylval.colno++;
145 /* check for special one-character constructions */
146 switch (c) {
147 case '\n':
148 yylval.colno = 0;
149 yylval.lineno++;
150 /* fallthrough */
151 case '{':
152 case '}':
153 case '=':
154 return c;
155 case '#':
156 /* skip comments; NUL is allowed; no continuation */
157 while ((c = getc(yyfp)) != '\n')
158 if (c == EOF)
159 goto eof;
160 yylval.colno = 0;
161 yylval.lineno++;
162 return c;
163 case EOF:
164 goto eof;
167 /* parsing next word */
168 for (;; c = getc(yyfp), yylval.colno++) {
169 switch (c) {
170 case '\0':
171 yyerror("unallowed character NULL in column %d",
172 yylval.colno+1);
173 escape = 0;
174 continue;
175 case '\\':
176 escape = !escape;
177 if (escape)
178 continue;
179 break;
180 case '\n':
181 if (quotes)
182 yyerror("unterminated quotes in column %d",
183 yylval.colno+1);
184 if (escape) {
185 nonkw = 1;
186 escape = 0;
187 yylval.colno = 0;
188 yylval.lineno++;
190 goto eow;
191 case EOF:
192 if (escape)
193 yyerror("unterminated escape in column %d",
194 yylval.colno);
195 if (quotes)
196 yyerror("unterminated quotes in column %d",
197 qpos + 1);
198 goto eow;
199 case '{':
200 case '}':
201 case '=':
202 case '#':
203 case ' ':
204 case '\t':
205 if (!escape && !quotes)
206 goto eow;
207 break;
208 case '"':
209 if (!escape) {
210 quotes = !quotes;
211 if (quotes) {
212 nonkw = 1;
213 qpos = yylval.colno;
215 continue;
218 *p++ = c;
219 if (p == ebuf) {
220 yyerror("line too long");
221 p = buf;
223 escape = 0;
226 eow:
227 *p = 0;
228 if (c != EOF)
229 ungetc(c, yyfp);
230 if (p == buf) {
231 /*
232 * There could be a number of reason for empty buffer,
233 * and we handle all of them here, to avoid cluttering
234 * the main loop.
235 */
236 if (c == EOF)
237 goto eof;
238 else if (qpos == -1) /* accept, e.g., empty args: cmd foo args "" */
239 goto repeat;
241 if (!nonkw) {
242 for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
243 if (strcmp(buf, keywords[i].word) == 0)
244 return keywords[i].token;
247 c = *buf;
248 if (!nonkw && (c == '-' || isdigit(c))) {
249 yylval.num = strtonum(buf, INT_MIN, INT_MAX, &errstr);
250 if (errstr != NULL)
251 yyerror("number is %s: %s", errstr, buf);
252 return TNUMBER;
254 if ((str = strdup(buf)) == NULL)
255 err(1, "%s", __func__);
256 yylval.str = str;
257 return TSTRING;
259 eof:
260 if (ferror(yyfp))
261 yyerror("input error reading config");
262 return 0;
265 void
266 parseconfig(const char *filename, int fonf)
268 if ((yyfp = fopen(filename, "r")) == NULL) {
269 if (fonf)
270 err(1, "%s", filename);
271 return;
274 path = filename;
275 yyparse();
276 fclose(yyfp);
277 if (parse_errors)
278 exit(1);