xref: /netbsd-src/external/bsd/bc/dist/scan.l (revision ed857e95db3fec367bb6764523110eb0ac99cb49)
1 /*	$NetBSD: scan.l,v 1.1 2017/04/10 02:28:23 phil Exp $ */
2 
3 /*
4  * Copyright (C) 1991-1994, 1997, 2006, 2008, 2012-2017 Free Software Foundation, Inc.
5  * Copyright (C) 2016-2017 Philip A. Nelson.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. The names Philip A. Nelson and Free Software Foundation may not be
18  *    used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY PHILIP A. NELSON ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL PHILIP A. NELSON OR THE FREE SOFTWARE FOUNDATION BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31  * THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /* scan.l: the (f)lex description file for the scanner. */
35 
36 %{
37 
38 #include "bcdefs.h"
39 #include "bc.h"
40 #include "global.h"
41 #include "proto.h"
42 #include <errno.h>
43 
44 /* Using flex, we can ask for a smaller input buffer.  With lex, this
45    does nothing! */
46 
47 #ifdef SMALL_BUF
48 #undef YY_READ_BUF_SIZE
49 #define YY_READ_BUF_SIZE 512
50 #endif
51 
52 /* Force . as last for now. */
53 #define DOT_IS_LAST
54 
55 /* We want to define our own yywrap. */
56 #undef yywrap
57 int yywrap (void);
58 
59 #if defined(LIBEDIT)
60 /* Support for the BSD libedit with history for
61    nicer input on the interactive part of input. */
62 
63 #include <histedit.h>
64 
65 /* Have input call the following function. */
66 #undef  YY_INPUT
67 #define YY_INPUT(buf,result,max_size) \
68 		bcel_input((char *)buf, (yy_size_t *)&result, max_size)
69 
70 /* Variables to help interface editline with bc. */
71 static const char *bcel_line = (char *)NULL;
72 static int   bcel_len = 0;
73 
74 /* bcel_input puts upto MAX characters into BUF with the number put in
75    BUF placed in *RESULT.  If the yy input file is the same as
76    stdin, use editline.  Otherwise, just read it.
77 */
78 
79 static void
bcel_input(char * buf,yy_size_t * result,int max)80 bcel_input (char *buf, yy_size_t  *result, int max)
81 {
82   ssize_t rdsize;
83   if (!edit || yyin != stdin)
84     {
85       while ( (rdsize = read( fileno(yyin), buf, max )) < 0 )
86         if (errno != EINTR)
87 	  {
88 	    yyerror( "read() in flex scanner failed" );
89 	    bc_exit (1);
90 	  }
91       *result = (yy_size_t) rdsize;
92       return;
93     }
94 
95   /* Do we need a new string? */
96   if (bcel_len == 0)
97     {
98       bcel_line = el_gets(edit, &bcel_len);
99       if (bcel_line == NULL) {
100 	/* end of file */
101 	*result = 0;
102 	bcel_len = 0;
103 	return;
104       }
105       if (bcel_len != 0)
106 	history (hist, &histev, H_ENTER, bcel_line);
107       fflush (stdout);
108     }
109 
110   if (bcel_len <= max)
111     {
112       strncpy (buf, bcel_line, bcel_len);
113       *result = bcel_len;
114       bcel_len = 0;
115     }
116   else
117     {
118       strncpy (buf, bcel_line, max);
119       *result = max;
120       bcel_line += max;
121       bcel_len -= max;
122     }
123 }
124 #endif
125 
126 #ifdef READLINE
127 /* Support for the readline and history libraries.  This allows
128    nicer input on the interactive part of input. */
129 
130 /* Have input call the following function. */
131 #undef  YY_INPUT
132 #define YY_INPUT(buf,result,max_size) \
133 		rl_input((char *)buf, &result, max_size)
134 
135 /* Variables to help interface readline with bc. */
136 static char *rl_line = (char *)NULL;
137 static char *rl_start = (char *)NULL;
138 static int   rl_len = 0;
139 
140 /* Definitions for readline access. */
141 extern FILE *rl_instream;
142 
143 /* rl_input puts upto MAX characters into BUF with the number put in
144    BUF placed in *RESULT.  If the yy input file is the same as
145    rl_instream (stdin), use readline.  Otherwise, just read it.
146 */
147 
148 static void
rl_input(char * buf,int * result,int max)149 rl_input (char *buf, int *result, int max)
150 {
151   if (yyin != rl_instream)
152     {
153       while ( (*result = read( fileno(yyin), buf, max )) < 0 )
154         if (errno != EINTR)
155 	  {
156 	    yyerror( "read() in flex scanner failed" );
157 	    bc_exit (1);
158 	  }
159       return;
160     }
161 
162   /* Do we need a new string? */
163   if (rl_len == 0)
164     {
165       if (rl_start)
166 	free(rl_start);
167       rl_start = readline ("");
168       if (rl_start == NULL) {
169 	/* end of file */
170 	*result = 0;
171 	rl_len = 0;
172 	return;
173       }
174       rl_line = rl_start;
175       rl_len = strlen (rl_line)+1;
176       if (rl_len != 1)
177 	add_history (rl_line);
178       rl_line[rl_len-1] = '\n';
179       fflush (stdout);
180     }
181 
182   if (rl_len <= max)
183     {
184       strncpy (buf, rl_line, rl_len);
185       *result = rl_len;
186       rl_len = 0;
187     }
188   else
189     {
190       strncpy (buf, rl_line, max);
191       *result = max;
192       rl_line += max;
193       rl_len -= max;
194     }
195 }
196 #endif
197 
198 #if !defined(READLINE) && !defined(LIBEDIT)
199 
200 /* MINIX returns from read with < 0 if SIGINT is  encountered.
201    In flex, we can redefine YY_INPUT to the following.  In lex, this
202    does nothing! */
203 #undef  YY_INPUT
204 #define YY_INPUT(buf,result,max_size) \
205 	while ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \
206 	    if (errno != EINTR) \
207 		YY_FATAL_ERROR( "read() in flex scanner failed" );
208 #endif
209 
210 %}
211 DIGIT [0-9A-Z]
212 LETTER [a-z]
213 %s slcomment
214 %%
215 "#"		{
216  		  if (!std_only)
217 		    BEGIN(slcomment);
218  		  else
219 		    yyerror ("illegal character: #");
220 		}
221 <slcomment>[^\n]* { BEGIN(INITIAL); }
222 <slcomment>"\n" { line_no++; BEGIN(INITIAL); return(ENDOFLINE); }
223 define return(Define);
224 break  return(Break);
225 quit   return(Quit);
226 length return(Length);
227 return return(Return);
228 for    return(For);
229 if     return(If);
230 while  return(While);
231 sqrt   return(Sqrt);
232 scale  return(Scale);
233 ibase  return(Ibase);
234 obase  return(Obase);
235 auto   return(Auto);
236 else   return(Else);
237 read   return(Read);
238 random return(Random);
239 halt   return(Halt);
240 last   return(Last);
241 void   return(Void);
242 history {
243 #if defined(READLINE) || defined(LIBEDIT)
244 	  return(HistoryVar);
245 #else
246 	  yylval.s_value = strcopyof(yytext); return(NAME);
247 #endif
248 	}
249 
250 warranty return(Warranty);
251 continue return(Continue);
252 print  return(Print);
253 limits return(Limits);
254 "." {
255 #ifdef DOT_IS_LAST
256        return(Last);
257 #else
258        yyerror ("illegal character: %s",yytext);
259 #endif
260     }
261 "+"|"-"|";"|"("|")"|"{"|"}"|"["|"]"|","|"^" { yylval.c_value = yytext[0];
262 					      return((int)yytext[0]); }
263 && { return(AND); }
264 \|\| { return(OR); }
265 "!" { return(NOT); }
266 "*"|"/"|"%"|"&" { yylval.c_value = yytext[0]; return((int)yytext[0]); }
267 "="|\+=|-=|\*=|\/=|%=|\^=  { yylval.c_value = yytext[0]; return(ASSIGN_OP); }
268 =\+|=-|=\*|=\/|=%|=\^  {
269 #ifdef OLD_EQ_OP
270 			 char warn_save;
271 			 warn_save = warn_not_std;
272 			 warn_not_std = TRUE;
273 			 ct_warn ("Old fashioned =<op>");
274 			 warn_not_std = warn_save;
275 			 yylval.c_value = yytext[1];
276 #else
277 			 yylval.c_value = '=';
278 			 yyless (1);
279 #endif
280 			 return(ASSIGN_OP);
281 		       }
282 ==|\<=|\>=|\!=|"<"|">" { yylval.s_value = strcopyof(yytext); return(REL_OP); }
283 \+\+|-- { yylval.c_value = yytext[0]; return(INCR_DECR); }
284 "\n" { line_no++; return(ENDOFLINE); }
285 \\\n {  line_no++;  /* ignore a "quoted" newline */ }
286 [ \t]+  { /* ignore spaces and tabs */ }
287 "/*"  {
288 	int c;
289 
290 	for (;;)
291 	  {
292 	    while ( ((c=input()) != '*') && (c != EOF))
293 	      /* eat it */
294 	      if (c == '\n') line_no++;
295 	    if (c == '*')
296  	      {
297 		while ( (c=input()) == '*') /* eat it*/;
298 		if (c == '/') break; /* at end of comment */
299 		if (c == '\n') line_no++;
300 	      }
301 	    if (c == EOF)
302 	      {
303 		fprintf (stderr,"EOF encountered in a comment.\n");
304 		break;
305 	      }
306 	  }
307       }
308 [a-z][a-z0-9_]* { yylval.s_value = strcopyof(yytext); return(NAME); }
309 \"[^\"]*\"  {
310  	      const char *look;
311 	      int count = 0;
312 	      yylval.s_value = strcopyof(yytext);
313 	      for (look = yytext; *look != 0; look++)
314 		{
315 		  if (*look == '\n') line_no++;
316 		  if (*look == '"')  count++;
317 		}
318 	      if (count != 2) yyerror ("NUL character in string.");
319 	      return(STRING);
320 	    }
321 {DIGIT}({DIGIT}|\\\n)*("."({DIGIT}|\\\n)*)?|"."(\\\n)*{DIGIT}({DIGIT}|\\\n)* {
322 	      char *src, *dst;
323 	      int len;
324 	      /* remove a trailing decimal point. */
325 	      len = strlen(yytext);
326 	      if (yytext[len-1] == '.')
327 	        yytext[len-1] = 0;
328 	      /* remove leading zeros. */
329 	      src = yytext;
330 	      dst = yytext;
331 	      while (*src == '0') src++;
332 	      if (*src == 0) src--;
333 	      /* Copy strings removing the newlines. */
334 	      while (*src != 0)
335 		{
336 	          if (*src == '\\')
337 		    {
338 		      src++; src++;
339 		      line_no++;
340 		    }
341 		  if (*src == ',')
342 		    {
343 		      src++;
344 		      ct_warn("Commas in numbers");
345 		    }
346 		  else
347 		    *dst++ = *src++;
348 	        }
349 	      *dst = 0;
350 	      yylval.s_value = strcopyof(yytext);
351 	      return(NUMBER);
352 	    }
353 .       {
354 	  if (yytext[0] < ' ')
355 	    yyerror ("illegal character: ^%c",yytext[0] + '@');
356 	  else
357 	    if (yytext[0] > '~')
358 	      yyerror ("illegal character: \\%03o", (int) yytext[0]);
359 	    else
360 	      yyerror ("illegal character: %s",yytext);
361 	}
362 %%
363 
364 
365 
366 /* This is the way to get multiple files input into lex. */
367 
368 int
369 yywrap(void)
370 {
371   if (!open_new_file ()) return (1);	/* EOF on standard in. */
372   return (0);          			/* We have more input. */
373   yyunput(0,NULL);	/* Make sure the compiler think yyunput is used. */
374 }
375