1%{
2/* scan.l: the (f)lex description file for the scanner. */
3
4/*  This file is part of GNU bc.
5    Copyright (C) 1991, 1992, 1993, 1994, 1997 Free Software Foundation, Inc.
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License , or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; see the file COPYING.  If not, write to
19      The Free Software Foundation, Inc.
20      59 Temple Place, Suite 330
21      Boston, MA 02111 USA
22
23    You may contact the author by:
24       e-mail:  philnelson@acm.org
25      us-mail:  Philip A. Nelson
26                Computer Science Department, 9062
27                Western Washington University
28                Bellingham, WA 98226-9062
29
30*************************************************************************/
31
32#include "bcdefs.h"
33#include "bc.h"
34#include "global.h"
35#include "proto.h"
36#include <errno.h>
37
38/* Using flex, we can ask for a smaller input buffer.  With lex, this
39   does nothing! */
40
41#ifdef SMALL_BUF
42#undef YY_READ_BUF_SIZE
43#define YY_READ_BUF_SIZE 512
44#endif
45
46/* Force . as last for now. */
47#define DOT_IS_LAST
48
49/* We want to define our own yywrap. */
50#undef yywrap
51_PROTOTYPE(int yywrap, (void));
52
53#if defined(LIBEDIT)
54/* Support for the BSD libedit with history for
55   nicer input on the interactive part of input. */
56
57#include <histedit.h>
58
59/* Have input call the following function. */
60#undef  YY_INPUT
61#define YY_INPUT(buf,result,max_size) \
62		bcel_input((char *)buf, &result, max_size)
63
64/* Variables to help interface editline with bc. */
65static const char *bcel_line = (char *)NULL;
66static int   bcel_len = 0;
67
68
69/* Required to get rid of that ugly ? default prompt! */
70char *
71null_prompt (EditLine *el)
72{
73  return "";
74}
75
76
77/* bcel_input puts upto MAX characters into BUF with the number put in
78   BUF placed in *RESULT.  If the yy input file is the same as
79   stdin, use editline.  Otherwise, just read it.
80*/
81
82static void
83bcel_input (buf, result, max)
84	char *buf;
85	int  *result;
86	int   max;
87{
88  if (!edit || yyin != stdin)
89    {
90      while ( (*result = read( fileno(yyin), buf, max )) < 0 )
91        if (errno != EINTR)
92	  {
93	    yyerror( "read() in flex scanner failed" );
94	    exit (1);
95	  }
96      return;
97    }
98
99  /* Do we need a new string? */
100  if (bcel_len == 0)
101    {
102      bcel_line = el_gets(edit, &bcel_len);
103      if (bcel_line == NULL) {
104	/* end of file */
105	*result = 0;
106	bcel_len = 0;
107	return;
108      }
109      if (bcel_len != 0)
110	history (hist, &histev, H_ENTER, bcel_line);
111      fflush (stdout);
112    }
113
114  if (bcel_len <= max)
115    {
116      strncpy (buf, bcel_line, bcel_len);
117      *result = bcel_len;
118      bcel_len = 0;
119    }
120  else
121    {
122      strncpy (buf, bcel_line, max);
123      *result = max;
124      bcel_line += max;
125      bcel_len -= max;
126    }
127}
128#endif
129
130#ifdef READLINE
131/* Support for the readline and history libraries.  This allows
132   nicer input on the interactive part of input. */
133
134/* Have input call the following function. */
135#undef  YY_INPUT
136#define YY_INPUT(buf,result,max_size) \
137		rl_input((char *)buf, &result, max_size)
138
139/* Variables to help interface readline with bc. */
140static char *rl_line = (char *)NULL;
141static char *rl_start = (char *)NULL;
142static int   rl_len = 0;
143
144/* Definitions for readline access. */
145extern FILE *rl_instream;
146_PROTOTYPE(char *readline, (char *));
147
148/* rl_input puts upto MAX characters into BUF with the number put in
149   BUF placed in *RESULT.  If the yy input file is the same as
150   rl_instream (stdin), use readline.  Otherwise, just read it.
151*/
152
153static void
154rl_input (buf, result, max)
155	char *buf;
156	int  *result;
157	int   max;
158{
159  if (yyin != rl_instream)
160    {
161      while ( (*result = read( fileno(yyin), buf, max )) < 0 )
162        if (errno != EINTR)
163	  {
164	    yyerror( "read() in flex scanner failed" );
165	    exit (1);
166	  }
167      return;
168    }
169
170  /* Do we need a new string? */
171  if (rl_len == 0)
172    {
173      if (rl_start)
174	free(rl_start);
175      rl_start = readline ("");
176      if (rl_start == NULL) {
177	/* end of file */
178	*result = 0;
179	rl_len = 0;
180	return;
181      }
182      rl_line = rl_start;
183      rl_len = strlen (rl_line)+1;
184      if (rl_len != 1)
185	add_history (rl_line);
186      rl_line[rl_len-1] = '\n';
187      fflush (stdout);
188    }
189
190  if (rl_len <= max)
191    {
192      strncpy (buf, rl_line, rl_len);
193      *result = rl_len;
194      rl_len = 0;
195    }
196  else
197    {
198      strncpy (buf, rl_line, max);
199      *result = max;
200      rl_line += max;
201      rl_len -= max;
202    }
203}
204#endif
205
206#if !defined(READLINE) && !defined(LIBEDIT)
207
208/* MINIX returns from read with < 0 if SIGINT is  encountered.
209   In flex, we can redefine YY_INPUT to the following.  In lex, this
210   does nothing! */
211#undef  YY_INPUT
212#define YY_INPUT(buf,result,max_size) \
213	while ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \
214	    if (errno != EINTR) \
215		YY_FATAL_ERROR( "read() in flex scanner failed" );
216#endif
217
218%}
219DIGIT [0-9A-F]
220LETTER [a-z]
221%s slcomment
222%%
223"#"		{
224 		  if (!std_only)
225		    BEGIN(slcomment);
226 		  else
227		    yyerror ("illegal character: #");
228		}
229<slcomment>[^\n]* { BEGIN(INITIAL); }
230<slcomment>"\n" { line_no++; BEGIN(INITIAL); return(ENDOFLINE); }
231define return(Define);
232break  return(Break);
233quit   return(Quit);
234length return(Length);
235return return(Return);
236for    return(For);
237if     return(If);
238while  return(While);
239sqrt   return(Sqrt);
240scale  return(Scale);
241ibase  return(Ibase);
242obase  return(Obase);
243auto   return(Auto);
244else   return(Else);
245read   return(Read);
246halt   return(Halt);
247last   return(Last);
248history {
249#if defined(READLINE) || defined(LIBEDIT)
250	  return(HistoryVar);
251#else
252	  yylval.s_value = strcopyof(yytext); return(NAME);
253#endif
254	}
255
256warranty return(Warranty);
257continue return(Continue);
258print  return(Print);
259limits return(Limits);
260"." {
261#ifdef DOT_IS_LAST
262       return(Last);
263#else
264       yyerror ("illegal character: %s",yytext);
265#endif
266    }
267"+"|"-"|";"|"("|")"|"{"|"}"|"["|"]"|","|"^" { yylval.c_value = yytext[0];
268					      return((int)yytext[0]); }
269&& { return(AND); }
270\|\| { return(OR); }
271"!" { return(NOT); }
272"*"|"/"|"%" { yylval.c_value = yytext[0]; return((int)yytext[0]); }
273"="|\+=|-=|\*=|\/=|%=|\^=  { yylval.c_value = yytext[0]; return(ASSIGN_OP); }
274=\+|=-|=\*|=\/|=%|=\^  {
275#ifdef OLD_EQ_OP
276			 char warn_save;
277			 warn_save = warn_not_std;
278			 warn_not_std = TRUE;
279			 warn ("Old fashioned =<op>");
280			 warn_not_std = warn_save;
281			 yylval.c_value = yytext[1];
282#else
283			 yylval.c_value = '=';
284			 yyless (1);
285#endif
286			 return(ASSIGN_OP);
287		       }
288==|\<=|\>=|\!=|"<"|">" { yylval.s_value = strcopyof(yytext); return(REL_OP); }
289\+\+|-- { yylval.c_value = yytext[0]; return(INCR_DECR); }
290"\n" { line_no++; return(ENDOFLINE); }
291\\\n {  line_no++;  /* ignore a "quoted" newline */ }
292[ \t]+  { /* ignore spaces and tabs */ }
293"/*"  {
294	int c;
295
296	for (;;)
297	  {
298	    while ( ((c=input()) != '*') && (c != EOF))
299	      /* eat it */
300	      if (c == '\n') line_no++;
301	    if (c == '*')
302 	      {
303		while ( (c=input()) == '*') /* eat it*/;
304		if (c == '/') break; /* at end of comment */
305		if (c == '\n') line_no++;
306	      }
307	    if (c == EOF)
308	      {
309		fprintf (stderr,"EOF encountered in a comment.\n");
310		break;
311	      }
312	  }
313      }
314[a-z][a-z0-9_]* { yylval.s_value = strcopyof(yytext); return(NAME); }
315\"[^\"]*\"  {
316 	      unsigned char *look;
317	      int count = 0;
318	      yylval.s_value = strcopyof(yytext);
319	      for (look = yytext; *look != 0; look++)
320		{
321		  if (*look == '\n') line_no++;
322		  if (*look == '"')  count++;
323		}
324	      if (count != 2) yyerror ("NUL character in string.");
325	      return(STRING);
326	    }
327{DIGIT}({DIGIT}|\\\n)*("."({DIGIT}|\\\n)*)?|"."(\\\n)*{DIGIT}({DIGIT}|\\\n)* {
328	      unsigned char *src, *dst;
329	      int len;
330	      /* remove a trailing decimal point. */
331	      len = strlen(yytext);
332	      if (yytext[len-1] == '.')
333	        yytext[len-1] = 0;
334	      /* remove leading zeros. */
335	      src = yytext;
336	      dst = yytext;
337	      while (*src == '0') src++;
338	      if (*src == 0) src--;
339	      /* Copy strings removing the newlines. */
340	      while (*src != 0)
341		{
342	          if (*src == '\\')
343		    {
344		      src++; src++;
345		      line_no++;
346		    }
347		  else
348		    *dst++ = *src++;
349	        }
350	      *dst = 0;
351	      yylval.s_value = strcopyof(yytext);
352	      return(NUMBER);
353	    }
354.       {
355	  if (yytext[0] < ' ')
356	    yyerror ("illegal character: ^%c",yytext[0] + '@');
357	  else
358	    if (yytext[0] > '~')
359	      yyerror ("illegal character: \\%03o", (int) yytext[0]);
360	    else
361	      yyerror ("illegal character: %s",yytext);
362	}
363%%
364
365
366
367/* This is the way to get multiple files input into lex. */
368
369int
370yywrap()
371{
372  if (!open_new_file ()) return (1);	/* EOF on standard in. */
373  return (0);				/* We have more input. */
374}
375