/* $NetBSD: scan.l,v 1.1 2017/04/10 02:28:23 phil Exp $ */
/*
* Copyright (C) 1991-1994, 1997, 2006, 2008, 2012-2017 Free Software Foundation, Inc.
* Copyright (C) 2016-2017 Philip A. Nelson.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The names Philip A. Nelson and Free Software Foundation may not be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY PHILIP A. NELSON ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL PHILIP A. NELSON OR THE FREE SOFTWARE FOUNDATION BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
/* scan.l: the (f)lex description file for the scanner. */
%{
#include "bcdefs.h"
#include "bc.h"
#include "global.h"
#include "proto.h"
#include <errno.h>
/* Using flex, we can ask for a smaller input buffer. With lex, this
does nothing! */
#ifdef SMALL_BUF
#undef YY_READ_BUF_SIZE
#define YY_READ_BUF_SIZE 512
#endif
/* Force . as last for now. */
#define DOT_IS_LAST
/* We want to define our own yywrap. */
#undef yywrap
int yywrap (void);
#if defined(LIBEDIT)
/* Support for the BSD libedit with history for
nicer input on the interactive part of input. */
#include <histedit.h>
/* Have input call the following function. */
#undef YY_INPUT
#define YY_INPUT(buf,result,max_size) \
bcel_input((char *)buf, (yy_size_t *)&result, max_size)
/* Variables to help interface editline with bc. */
static const char *bcel_line = (char *)NULL;
static int bcel_len = 0;
/* bcel_input puts upto MAX characters into BUF with the number put in
BUF placed in *RESULT. If the yy input file is the same as
stdin, use editline. Otherwise, just read it.
*/
static void
bcel_input (char *buf, yy_size_t *result, int max)
{
ssize_t rdsize;
if (!edit || yyin != stdin)
{
while ( (rdsize = read( fileno(yyin), buf, max )) < 0 )
if (errno != EINTR)
{
yyerror( "read() in flex scanner failed" );
bc_exit (1);
}
*result = (yy_size_t) rdsize;
return;
}
/* Do we need a new string? */
if (bcel_len == 0)
{
bcel_line = el_gets(edit, &bcel_len);
if (bcel_line == NULL) {
/* end of file */
*result = 0;
bcel_len = 0;
return;
}
if (bcel_len != 0)
history (hist, &histev, H_ENTER, bcel_line);
fflush (stdout);
}
if (bcel_len <= max)
{
strncpy (buf, bcel_line, bcel_len);
*result = bcel_len;
bcel_len = 0;
}
else
{
strncpy (buf, bcel_line, max);
*result = max;
bcel_line += max;
bcel_len -= max;
}
}
#endif
#ifdef READLINE
/* Support for the readline and history libraries. This allows
nicer input on the interactive part of input. */
/* Have input call the following function. */
#undef YY_INPUT
#define YY_INPUT(buf,result,max_size) \
rl_input((char *)buf, &result, max_size)
/* Variables to help interface readline with bc. */
static char *rl_line = (char *)NULL;
static char *rl_start = (char *)NULL;
static int rl_len = 0;
/* Definitions for readline access. */
extern FILE *rl_instream;
/* rl_input puts upto MAX characters into BUF with the number put in
BUF placed in *RESULT. If the yy input file is the same as
rl_instream (stdin), use readline. Otherwise, just read it.
*/
static void
rl_input (char *buf, int *result, int max)
{
if (yyin != rl_instream)
{
while ( (*result = read( fileno(yyin), buf, max )) < 0 )
if (errno != EINTR)
{
yyerror( "read() in flex scanner failed" );
bc_exit (1);
}
return;
}
/* Do we need a new string? */
if (rl_len == 0)
{
if (rl_start)
free(rl_start);
rl_start = readline ("");
if (rl_start == NULL) {
/* end of file */
*result = 0;
rl_len = 0;
return;
}
rl_line = rl_start;
rl_len = strlen (rl_line)+1;
if (rl_len != 1)
add_history (rl_line);
rl_line[rl_len-1] = '\n';
fflush (stdout);
}
if (rl_len <= max)
{
strncpy (buf, rl_line, rl_len);
*result = rl_len;
rl_len = 0;
}
else
{
strncpy (buf, rl_line, max);
*result = max;
rl_line += max;
rl_len -= max;
}
}
#endif
#if !defined(READLINE) && !defined(LIBEDIT)
/* MINIX returns from read with < 0 if SIGINT is encountered.
In flex, we can redefine YY_INPUT to the following. In lex, this
does nothing! */
#undef YY_INPUT
#define YY_INPUT(buf,result,max_size) \
while ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \
if (errno != EINTR) \
YY_FATAL_ERROR( "read() in flex scanner failed" );
#endif
%}
DIGIT [0-9A-Z]
LETTER [a-z]
%s slcomment
%%
"#" {
if (!std_only)
BEGIN(slcomment);
else
yyerror ("illegal character: #");
}
<slcomment>[^\n]* { BEGIN(INITIAL); }
<slcomment>"\n" { line_no++; BEGIN(INITIAL); return(ENDOFLINE); }
define return(Define);
break return(Break);
quit return(Quit);
length return(Length);
return return(Return);
for return(For);
if return(If);
while return(While);
sqrt return(Sqrt);
scale return(Scale);
ibase return(Ibase);
obase return(Obase);
auto return(Auto);
else return(Else);
read return(Read);
random return(Random);
halt return(Halt);
last return(Last);
void return(Void);
history {
#if defined(READLINE) || defined(LIBEDIT)
return(HistoryVar);
#else
yylval.s_value = strcopyof(yytext); return(NAME);
#endif
}
warranty return(Warranty);
continue return(Continue);
print return(Print);
limits return(Limits);
"." {
#ifdef DOT_IS_LAST
return(Last);
#else
yyerror ("illegal character: %s",yytext);
#endif
}
"+"|"-"|";"|"("|")"|"{"|"}"|"["|"]"|","|"^" { yylval.c_value = yytext[0];
return((int)yytext[0]); }
&& { return(AND); }
\|\| { return(OR); }
"!" { return(NOT); }
"*"|"/"|"%"|"&" { yylval.c_value = yytext[0]; return((int)yytext[0]); }
"="|\+=|-=|\*=|\/=|%=|\^= { yylval.c_value = yytext[0]; return(ASSIGN_OP); }
=\+|=-|=\*|=\/|=%|=\^ {
#ifdef OLD_EQ_OP
char warn_save;
warn_save = warn_not_std;
warn_not_std = TRUE;
ct_warn ("Old fashioned =<op>");
warn_not_std = warn_save;
yylval.c_value = yytext[1];
#else
yylval.c_value = '=';
yyless (1);
#endif
return(ASSIGN_OP);
}
==|\<=|\>=|\!=|"<"|">" { yylval.s_value = strcopyof(yytext); return(REL_OP); }
\+\+|-- { yylval.c_value = yytext[0]; return(INCR_DECR); }
"\n" { line_no++; return(ENDOFLINE); }
\\\n { line_no++; /* ignore a "quoted" newline */ }
[ \t]+ { /* ignore spaces and tabs */ }
"/*" {
int c;
for (;;)
{
while ( ((c=input()) != '*') && (c != EOF))
/* eat it */
if (c == '\n') line_no++;
if (c == '*')
{
while ( (c=input()) == '*') /* eat it*/;
if (c == '/') break; /* at end of comment */
if (c == '\n') line_no++;
}
if (c == EOF)
{
fprintf (stderr,"EOF encountered in a comment.\n");
break;
}
}
}
[a-z][a-z0-9_]* { yylval.s_value = strcopyof(yytext); return(NAME); }
\"[^\"]*\" {
const char *look;
int count = 0;
yylval.s_value = strcopyof(yytext);
for (look = yytext; *look != 0; look++)
{
if (*look == '\n') line_no++;
if (*look == '"') count++;
}
if (count != 2) yyerror ("NUL character in string.");
return(STRING);
}
{DIGIT}({DIGIT}|\\\n)*("."({DIGIT}|\\\n)*)?|"."(\\\n)*{DIGIT}({DIGIT}|\\\n)* {
char *src, *dst;
int len;
/* remove a trailing decimal point. */
len = strlen(yytext);
if (yytext[len-1] == '.')
yytext[len-1] = 0;
/* remove leading zeros. */
src = yytext;
dst = yytext;
while (*src == '0') src++;
if (*src == 0) src--;
/* Copy strings removing the newlines. */
while (*src != 0)
{
if (*src == '\\')
{
src++; src++;
line_no++;
}
if (*src == ',')
{
src++;
ct_warn("Commas in numbers");
}
else
*dst++ = *src++;
}
*dst = 0;
yylval.s_value = strcopyof(yytext);
return(NUMBER);
}
. {
if (yytext[0] < ' ')
yyerror ("illegal character: ^%c",yytext[0] + '@');
else
if (yytext[0] > '~')
yyerror ("illegal character: \\%03o", (int) yytext[0]);
else
yyerror ("illegal character: %s",yytext);
}
%%
/* This is the way to get multiple files input into lex. */
int
yywrap(void)
{
if (!open_new_file ()) return (1); /* EOF on standard in. */
return (0); /* We have more input. */
yyunput(0,NULL); /* Make sure the compiler think yyunput is used. */
}