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
57int 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. */
71static const char *bcel_line = (char *)NULL;
72static 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
79static void
80bcel_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. */
136static char *rl_line = (char *)NULL;
137static char *rl_start = (char *)NULL;
138static int   rl_len = 0;
139
140/* Definitions for readline access. */
141extern 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
148static void
149rl_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%}
211DIGIT [0-9A-Z]
212LETTER [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); }
223define return(Define);
224break  return(Break);
225quit   return(Quit);
226length return(Length);
227return return(Return);
228for    return(For);
229if     return(If);
230while  return(While);
231sqrt   return(Sqrt);
232scale  return(Scale);
233ibase  return(Ibase);
234obase  return(Obase);
235auto   return(Auto);
236else   return(Else);
237read   return(Read);
238random return(Random);
239halt   return(Halt);
240last   return(Last);
241void   return(Void);
242history {
243#if defined(READLINE) || defined(LIBEDIT)
244	  return(HistoryVar);
245#else
246	  yylval.s_value = strcopyof(yytext); return(NAME);
247#endif
248	}
249
250warranty return(Warranty);
251continue return(Continue);
252print  return(Print);
253limits 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
368int
369yywrap(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