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