c8fe2b22b959ec419e8652439bebbec0701520c9
[dragonfly.git] / usr.bin / bc / scan.l
1 %{
2 /*
3  * $OpenBSD: scan.l,v 1.21 2006/03/18 20:44:43 otto Exp $
4  */
5
6 /*
7  * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net>
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21
22 #pragma GCC diagnostic ignored "-Wsign-compare"
23 #include <err.h>
24 #include <errno.h>
25 #include <histedit.h>
26 #include <signal.h>
27 #include <stdbool.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #include "extern.h"
32 #include "pathnames.h"
33 #include "y.tab.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 static int      bc_yyinput(char *, int);
51
52 #undef YY_INPUT
53 #define YY_INPUT(buf,retval,max) \
54         (retval = bc_yyinput(buf, max))
55
56 %}
57
58 %option always-interactive
59 %option noinput
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)
244 {
245         const char str1[] = "[\n]P\n";
246         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)
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, (char *)(uintptr_t)(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 }