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