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