xref: /openbsd-src/usr.bin/bc/scan.l (revision 5b133f3f277e80f096764111e64f3a1284acb179)
1 %{
2 /*      $OpenBSD: scan.l,v 1.32 2023/03/08 04:43:10 guenther Exp $	*/
3 
4 /*
5  * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net>
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 
20 #include <err.h>
21 #include <histedit.h>
22 #include <signal.h>
23 #include <string.h>
24 #include <unistd.h>
25 
26 #include "extern.h"
27 #include "pathnames.h"
28 #include "bc.h"
29 
30 int		lineno;
31 bool		interactive;
32 
33 HistEvent	 he;
34 EditLine	*el;
35 History		*hist;
36 
37 static char	*strbuf = NULL;
38 static size_t	strbuf_sz = 1;
39 static bool	dot_seen;
40 static int	use_el;
41 static volatile sig_atomic_t skipchars;
42 
43 static void	init_strbuf(void);
44 static void	add_str(const char *);
45 
46 static int	 bc_yyinput(char *, int);
47 
48 #undef YY_INPUT
49 #define YY_INPUT(buf,retval,max) \
50 	(retval = bc_yyinput(buf, max))
51 
52 %}
53 
54 %option always-interactive
55 
56 DIGIT		[0-9A-F]
57 ALPHA		[a-z_]
58 ALPHANUM	[a-z_0-9]
59 
60 %x		comment string number
61 
62 %%
63 
64 "/*"		BEGIN(comment);
65 <comment>{
66 	"*/"	BEGIN(INITIAL);
67 	\n	lineno++;
68 	\*	;
69 	[^*\n]+	;
70 	<<EOF>>	fatal("end of file in comment");
71 }
72 
73 \"		BEGIN(string); init_strbuf();
74 <string>{
75 	[^"\n\\\[\]]+	add_str(yytext);
76 	\[	add_str("\\[");
77 	\]	add_str("\\]");
78 	\\	add_str("\\\\");
79 	\n	add_str("\n"); lineno++;
80 	\"	BEGIN(INITIAL); yylval.str = strbuf; return STRING;
81 	<<EOF>>	fatal("end of file in string");
82 }
83 
84 {DIGIT}+	{
85 			BEGIN(number);
86 			dot_seen = false;
87 			init_strbuf();
88 			add_str(yytext);
89 		}
90 \.		{
91 			BEGIN(number);
92 			dot_seen = true;
93 			init_strbuf();
94 			add_str(".");
95 		}
96 <number>{
97 	{DIGIT}+	add_str(yytext);
98 	\.	{
99 			if (dot_seen) {
100 				BEGIN(INITIAL);
101 				yylval.str = strbuf;
102 				unput('.');
103 				return NUMBER;
104 			} else {
105 				dot_seen = true;
106 				add_str(".");
107 			}
108 		}
109 	\\\n[ \t]*	lineno++;
110 	[^0-9A-F\.]	{
111 			BEGIN(INITIAL);
112 			unput(yytext[0]);
113 			if (strcmp(strbuf, ".") == 0)
114 				return DOT;
115 			else {
116 				yylval.str = strbuf;
117 				return NUMBER;
118 			}
119 		}
120 }
121 
122 "auto"		return AUTO;
123 "break"		return BREAK;
124 "continue"	return CONTINUE;
125 "define"	return DEFINE;
126 "else"		return ELSE;
127 "ibase"		return IBASE;
128 "if"		return IF;
129 "last"		return DOT;
130 "for"		return FOR;
131 "length"	return LENGTH;
132 "obase"		return OBASE;
133 "print"		return PRINT;
134 "quit"		return QUIT;
135 "return"	return RETURN;
136 "scale"		return SCALE;
137 "sqrt"		return SQRT;
138 "while"		return WHILE;
139 
140 "^"		return EXPONENT;
141 "*"		return MULTIPLY;
142 "/"		return DIVIDE;
143 "%"		return REMAINDER;
144 
145 "!"		return BOOL_NOT;
146 "&&"		return BOOL_AND;
147 "||"		return BOOL_OR;
148 
149 "+"		return PLUS;
150 "-"		return MINUS;
151 
152 "++"		return INCR;
153 "--"		return DECR;
154 
155 "="		yylval.str = ""; return ASSIGN_OP;
156 "+="		yylval.str = "+"; return ASSIGN_OP;
157 "-="		yylval.str = "-"; return ASSIGN_OP;
158 "*="		yylval.str = "*"; return ASSIGN_OP;
159 "/="		yylval.str = "/"; return ASSIGN_OP;
160 "%="		yylval.str = "%"; return ASSIGN_OP;
161 "^="		yylval.str = "^"; return ASSIGN_OP;
162 
163 "=="		return EQUALS;
164 "<="		return LESS_EQ;
165 ">="		return GREATER_EQ;
166 "!="		return UNEQUALS;
167 "<"		return LESS;
168 ">"		return GREATER;
169 
170 ","		return COMMA;
171 ";"		return SEMICOLON;
172 
173 "("		return LPAR;
174 ")"		return RPAR;
175 
176 "["		return LBRACKET;
177 "]"		return RBRACKET;
178 
179 "{"		return LBRACE;
180 "}"		return RBRACE;
181 
182 {ALPHA}{ALPHANUM}* {
183 			/* alloc an extra byte for the type marker */
184 			char *p = malloc(yyleng + 2);
185 			if (p == NULL)
186 				err(1, NULL);
187 			strlcpy(p, yytext, yyleng + 1);
188 			yylval.astr = p;
189 			return LETTER;
190 		}
191 
192 \\\n		lineno++;
193 \n		lineno++; return NEWLINE;
194 
195 #[^\n]*		;
196 [ \t]		;
197 <<EOF>>		return QUIT;
198 .		yyerror("illegal character");
199 
200 %%
201 
202 static void
203 init_strbuf(void)
204 {
205 	if (strbuf == NULL) {
206 		strbuf = malloc(strbuf_sz);
207 		if (strbuf == NULL)
208 			err(1, NULL);
209 	}
210 	strbuf[0] = '\0';
211 }
212 
213 static void
214 add_str(const char *str)
215 {
216 	size_t arglen;
217 
218 	arglen = strlen(str);
219 
220 	if (strlen(strbuf) + arglen + 1 > strbuf_sz) {
221 		size_t newsize;
222 		char *p;
223 
224 		newsize = strbuf_sz + arglen + 1;
225 		p = realloc(strbuf, newsize);
226 		if (p == NULL) {
227 			free(strbuf);
228 			err(1, NULL);
229 		}
230 		strbuf_sz = newsize;
231 		strbuf = p;
232 	}
233 	strlcat(strbuf, str, strbuf_sz);
234 }
235 
236 void
237 abort_line(int sig)
238 {
239 	static const char str1[] = "[\n]P\n";
240 	static const char str2[] = "[^C\n]P\n";
241 	int save_errno;
242 	const LineInfo *info;
243 
244 	save_errno = errno;
245 	if (use_el) {
246 		write(STDOUT_FILENO, str2, sizeof(str2) - 1);
247 		/* XXX signal race */
248 		info = el_line(el);
249 		skipchars = info->lastchar - info->buffer;
250 	} else
251 		write(STDOUT_FILENO, str1, sizeof(str1) - 1);
252 	errno = save_errno;
253 }
254 
255 /*
256  * Avoid the echo of ^D by the default code of editline and take
257  * into account skipchars to make ^D work when the cursor is at start of
258  * line after a ^C.
259  */
260 unsigned char
261 bc_eof(EditLine *e, int ch)
262 {
263 	const struct lineinfo *info = el_line(e);
264 
265 	if (info->buffer + skipchars == info->cursor &&
266 	    info->cursor == info->lastchar)
267 		return (CC_EOF);
268 	else
269 		return (CC_ERROR);
270 }
271 
272 int
273 yywrap(void)
274 {
275 	static int state;
276 	static YY_BUFFER_STATE buf;
277 
278 	if (fileindex == 0 && sargc > 0 && strcmp(sargv[0], _PATH_LIBB) == 0) {
279 		filename = sargv[fileindex++];
280 		yyin = fopen(filename, "r");
281 		lineno = 1;
282 		if (yyin == NULL)
283 			err(1, "cannot open %s", filename);
284 		return (0);
285 	}
286 	if (state == 0 && cmdexpr[0] != '\0') {
287 		buf = yy_scan_string(cmdexpr);
288 		state++;
289 		lineno = 1;
290 		filename = "command line";
291 		return (0);
292 	} else if (state == 1) {
293 		yy_delete_buffer(buf);
294 		free(cmdexpr);
295 		state++;
296 	}
297 	if (yyin != NULL && yyin != stdin)
298 		fclose(yyin);
299 	if (fileindex < sargc) {
300 		filename = sargv[fileindex++];
301 		yyin = fopen(filename, "r");
302 		lineno = 1;
303 		if (yyin == NULL)
304 			err(1, "cannot open %s", filename);
305 		return (0);
306 	} else if (fileindex == sargc) {
307 		fileindex++;
308 		yyin = stdin;
309 		if (interactive) {
310 			signal(SIGINT, abort_line);
311 			signal(SIGTSTP, tstpcont);
312 		}
313 		lineno = 1;
314 		filename = "stdin";
315 		return (0);
316 	}
317 	return (1);
318 }
319 
320 static int
321 bc_yyinput(char *buf, int maxlen)
322 {
323 	int num;
324 
325 	if (el != NULL)
326 		el_get(el, EL_EDITMODE, &use_el);
327 
328 	if (yyin == stdin && interactive && use_el) {
329 		const char *bp;
330 		sigset_t oset, nset;
331 
332 		if ((bp = el_gets(el, &num)) == NULL || num == 0)
333 			return (0);
334 		sigemptyset(&nset);
335 		sigaddset(&nset, SIGINT);
336 		sigprocmask(SIG_BLOCK, &nset, &oset);
337 		if (skipchars < num) {
338 			bp += skipchars;
339 			num -= skipchars;
340 		}
341 		skipchars = 0;
342 		sigprocmask(SIG_SETMASK, &oset, NULL);
343 		if (num > maxlen) {
344 			el_push(el, (char *)(void *)bp + maxlen);
345 			num = maxlen;
346 		}
347 		memcpy(buf, bp, num);
348 		history(hist, &he, H_ENTER, bp);
349 		el_get(el, EL_EDITMODE, &use_el);
350 	} else {
351 		int c = '*';
352 		for (num = 0; num < maxlen &&
353 		    (c = getc(yyin)) != EOF && c != '\n'; ++num)
354 			buf[num] = (char) c;
355 		if (c == '\n')
356 			buf[num++] = (char) c;
357 		if (c == EOF && ferror(yyin))
358 			YY_FATAL_ERROR( "input in flex scanner failed" );
359 	}
360 	return (num);
361 }
362 
363 
364