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