Implement missing shell arithmetic operators in $(()) expansion

and variable recognition.

PR:		standards/52972
Submitted by:	Wartan Hachaturow <wart@tepkom.ru>
Reviewed by:	tjr (improved on original patch)
Tested by:	buildworld on CURRENT.
MFC after:	6 weeks
This commit is contained in:
Jens Schweikhardt 2003-08-30 12:31:44 +00:00
parent 9cd8ed99ee
commit bc411549ba
4 changed files with 244 additions and 57 deletions

View File

@ -34,5 +34,6 @@
* $FreeBSD$
*/
int arith_assign(char *, arith_t);
int arith(char *);
int expcmd(int , char **);

View File

@ -1,58 +1,4 @@
%token ARITH_NUM ARITH_LPAREN ARITH_RPAREN
%left ARITH_OR
%left ARITH_AND
%left ARITH_BOR
%left ARITH_BXOR
%left ARITH_BAND
%left ARITH_EQ ARITH_NE
%left ARITH_LT ARITH_GT ARITH_GE ARITH_LE
%left ARITH_LSHIFT ARITH_RSHIFT
%left ARITH_ADD ARITH_SUB
%left ARITH_MUL ARITH_DIV ARITH_REM
%left ARITH_UNARYMINUS ARITH_UNARYPLUS ARITH_NOT ARITH_BNOT
%%
exp: expr = {
return ($1);
}
;
expr: ARITH_LPAREN expr ARITH_RPAREN = { $$ = $2; }
| expr ARITH_OR expr = { $$ = $1 ? $1 : $3 ? $3 : 0; }
| expr ARITH_AND expr = { $$ = $1 ? ( $3 ? $3 : 0 ) : 0; }
| expr ARITH_BOR expr = { $$ = $1 | $3; }
| expr ARITH_BXOR expr = { $$ = $1 ^ $3; }
| expr ARITH_BAND expr = { $$ = $1 & $3; }
| expr ARITH_EQ expr = { $$ = $1 == $3; }
| expr ARITH_GT expr = { $$ = $1 > $3; }
| expr ARITH_GE expr = { $$ = $1 >= $3; }
| expr ARITH_LT expr = { $$ = $1 < $3; }
| expr ARITH_LE expr = { $$ = $1 <= $3; }
| expr ARITH_NE expr = { $$ = $1 != $3; }
| expr ARITH_LSHIFT expr = { $$ = $1 << $3; }
| expr ARITH_RSHIFT expr = { $$ = $1 >> $3; }
| expr ARITH_ADD expr = { $$ = $1 + $3; }
| expr ARITH_SUB expr = { $$ = $1 - $3; }
| expr ARITH_MUL expr = { $$ = $1 * $3; }
| expr ARITH_DIV expr = {
if ($3 == 0)
yyerror("division by zero");
$$ = $1 / $3;
}
| expr ARITH_REM expr = {
if ($3 == 0)
yyerror("division by zero");
$$ = $1 % $3;
}
| ARITH_NOT expr = { $$ = !($2); }
| ARITH_BNOT expr = { $$ = ~($2); }
| ARITH_SUB expr %prec ARITH_UNARYMINUS = { $$ = -($2); }
| ARITH_ADD expr %prec ARITH_UNARYPLUS = { $$ = $2; }
| ARITH_NUM
;
%%
%{
/*-
* Copyright (c) 1993
* The Regents of the University of California. All rights reserved.
@ -97,17 +43,207 @@ static char sccsid[] = "@(#)arith.y 8.3 (Berkeley) 5/4/95";
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <limits.h>
#include "shell.h"
#include "var.h"
%}
%union {
arith_t l_value;
char* s_value;
}
%token <l_value> ARITH_NUM ARITH_LPAREN ARITH_RPAREN
%token <s_value> ARITH_VAR
%type <l_value> expr
%right ARITH_ASSIGN
%right ARITH_ADDASSIGN ARITH_SUBASSIGN
%right ARITH_MULASSIGN ARITH_DIVASSIGN ARITH_REMASSIGN
%right ARITH_RSHASSIGN ARITH_LSHASSIGN
%right ARITH_BANDASSIGN ARITH_BXORASSIGN ARITH_BORASSIGN
%left ARITH_OR
%left ARITH_AND
%left ARITH_BOR
%left ARITH_BXOR
%left ARITH_BAND
%left ARITH_EQ ARITH_NE
%left ARITH_LT ARITH_GT ARITH_GE ARITH_LE
%left ARITH_LSHIFT ARITH_RSHIFT
%left ARITH_ADD ARITH_SUB
%left ARITH_MUL ARITH_DIV ARITH_REM
%left ARITH_UNARYMINUS ARITH_UNARYPLUS ARITH_NOT ARITH_BNOT
%%
exp: expr = {
return ($1);
}
;
expr: ARITH_LPAREN expr ARITH_RPAREN = { $$ = $2; }
| expr ARITH_OR expr = { $$ = $1 ? $1 : $3 ? $3 : 0; }
| expr ARITH_AND expr = { $$ = $1 ? ( $3 ? $3 : 0 ) : 0; }
| expr ARITH_BOR expr = { $$ = $1 | $3; }
| expr ARITH_BXOR expr = { $$ = $1 ^ $3; }
| expr ARITH_BAND expr = { $$ = $1 & $3; }
| expr ARITH_EQ expr = { $$ = $1 == $3; }
| expr ARITH_GT expr = { $$ = $1 > $3; }
| expr ARITH_GE expr = { $$ = $1 >= $3; }
| expr ARITH_LT expr = { $$ = $1 < $3; }
| expr ARITH_LE expr = { $$ = $1 <= $3; }
| expr ARITH_NE expr = { $$ = $1 != $3; }
| expr ARITH_LSHIFT expr = { $$ = $1 << $3; }
| expr ARITH_RSHIFT expr = { $$ = $1 >> $3; }
| expr ARITH_ADD expr = { $$ = $1 + $3; }
| expr ARITH_SUB expr = { $$ = $1 - $3; }
| expr ARITH_MUL expr = { $$ = $1 * $3; }
| expr ARITH_DIV expr = {
if ($3 == 0)
yyerror("division by zero");
$$ = $1 / $3;
}
| expr ARITH_REM expr = {
if ($3 == 0)
yyerror("division by zero");
$$ = $1 % $3;
}
| ARITH_NOT expr = { $$ = !($2); }
| ARITH_BNOT expr = { $$ = ~($2); }
| ARITH_SUB expr %prec ARITH_UNARYMINUS = { $$ = -($2); }
| ARITH_ADD expr %prec ARITH_UNARYPLUS = { $$ = $2; }
| ARITH_NUM
| ARITH_VAR {
char *p;
arith_t arith_val;
char *str_val;
if (lookupvar($1) == NULL)
setvarsafe($1, "0", 0);
str_val = lookupvar($1);
arith_val = strtoarith_t(str_val, &p, 0);
/*
* Conversion is successful only in case
* we've converted _all_ characters.
*/
if (*p != '\0')
yyerror("variable conversion error");
$$ = arith_val;
}
| ARITH_VAR ARITH_ASSIGN expr {
if (arith_assign($1, $3) != 1)
yyerror("variable assignment error");
$$ = $3;
}
| ARITH_VAR ARITH_ADDASSIGN expr {
arith_t value;
value = atoarith_t(lookupvar($1)) + $3;
if (arith_assign($1, value) != 0)
yyerror("variable assignment error");
$$ = value;
}
| ARITH_VAR ARITH_SUBASSIGN expr {
arith_t value;
value = atoarith_t(lookupvar($1)) - $3;
if (arith_assign($1, value) != 0)
yyerror("variable assignment error");
$$ = value;
}
| ARITH_VAR ARITH_MULASSIGN expr {
arith_t value;
value = atoarith_t(lookupvar($1)) * $3;
if (arith_assign($1, value) != 0)
yyerror("variable assignment error");
$$ = value;
}
| ARITH_VAR ARITH_DIVASSIGN expr {
arith_t value;
if ($3 == 0)
yyerror("division by zero");
value = atoarith_t(lookupvar($1)) / $3;
if (arith_assign($1, value) != 0)
yyerror("variable assignment error");
$$ = value;
}
| ARITH_VAR ARITH_REMASSIGN expr {
arith_t value;
if ($3 == 0)
yyerror("division by zero");
value = atoarith_t(lookupvar($1)) % $3;
if (arith_assign($1, value) != 0)
yyerror("variable assignment error");
$$ = value;
}
| ARITH_VAR ARITH_RSHASSIGN expr {
arith_t value;
value = atoarith_t(lookupvar($1)) >> $3;
if (arith_assign($1, value) != 0)
yyerror("variable assignment error");
$$ = value;
}
| ARITH_VAR ARITH_LSHASSIGN expr {
arith_t value;
value = atoarith_t(lookupvar($1)) << $3;
if (arith_assign($1, value) != 0)
yyerror("variable assignment error");
$$ = value;
}
| ARITH_VAR ARITH_BANDASSIGN expr {
arith_t value;
value = atoarith_t(lookupvar($1)) & $3;
if (arith_assign($1, value) != 0)
yyerror("variable assignment error");
$$ = value;
}
| ARITH_VAR ARITH_BXORASSIGN expr {
arith_t value;
value = atoarith_t(lookupvar($1)) ^ $3;
if (arith_assign($1, value) != 0)
yyerror("variable assignment error");
$$ = value;
}
| ARITH_VAR ARITH_BORASSIGN expr {
arith_t value;
value = atoarith_t(lookupvar($1)) | $3;
if (arith_assign($1, value) != 0)
yyerror("variable assignment error");
$$ = value;
}
;
%%
#include "error.h"
#include "output.h"
#include "memalloc.h"
#define lstrlen(var) (3 + (2 + CHAR_BIT * sizeof((var))) / 3)
char *arith_buf, *arith_startbuf;
extern void arith_lex_reset();
int yylex(void);
int yyparse(void);
int
arith_assign(char *name, arith_t value) {
char *str;
int ret;
str = (char *)ckmalloc(lstrlen(value));
sprintf(str, ARITH_FORMAT_STR, value);
ret = setvarsafe(name, str, 0);
free(str);
return ret;
}
int
arith(char *s)
{

View File

@ -43,10 +43,12 @@ static char sccsid[] = "@(#)arith_lex.l 8.3 (Berkeley) 5/4/95";
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "shell.h"
#include "y.tab.h"
#include "error.h"
#include "var.h"
#include "memalloc.h"
extern int yylval;
extern char *arith_buf, *arith_startbuf;
#undef YY_INPUT
#define YY_INPUT(buf,result,max) \
@ -56,7 +58,36 @@ extern char *arith_buf, *arith_startbuf;
%%
[ \t\n] { ; }
[0-9]+ { yylval = atol(yytext); return(ARITH_NUM); }
0x[a-fA-F0-9]+ {
yylval.l_value = strtoarith_t(yytext, NULL, 16);
return(ARITH_NUM);
}
0[0-7]+ {
yylval.l_value = strtoarith_t(yytext, NULL, 8);
return(ARITH_NUM);
}
[0-9]+ {
yylval.l_value = strtoarith_t(yytext, NULL, 10);
return(ARITH_NUM);
}
[A-Za-z][A-Za-z0-9_]* {
/*
* If variable doesn't exist, we should initialize
* it to zero.
*/
char *temp;
if (lookupvar(yytext) == NULL)
setvarsafe(yytext, "0", 0);
temp = (char *)ckmalloc(strlen(yytext) + 1);
yylval.s_value = strcpy(temp, yytext);
return(ARITH_VAR);
}
"(" { return(ARITH_LPAREN); }
")" { return(ARITH_RPAREN); }
"||" { return(ARITH_OR); }
@ -79,6 +110,17 @@ extern char *arith_buf, *arith_startbuf;
"-" { return(ARITH_SUB); }
"~" { return(ARITH_BNOT); }
"!" { return(ARITH_NOT); }
"=" { return(ARITH_ASSIGN); }
"+=" { return(ARITH_ADDASSIGN); }
"-=" { return(ARITH_SUBASSIGN); }
"*=" { return(ARITH_MULASSIGN); }
"/=" { return(ARITH_DIVASSIGN); }
"%=" { return(ARITH_REMASSIGN); }
">>=" { return(ARITH_RSHASSIGN); }
"<<=" { return(ARITH_LSHASSIGN); }
"&=" { return(ARITH_BANDASSIGN); }
"^=" { return(ARITH_BXORASSIGN); }
"|=" { return(ARITH_BORASSIGN); }
. { error("arith: syntax error: \"%s\"\n", arith_startbuf); }
%%

View File

@ -51,6 +51,14 @@
#define JOBS 1
/* #define DEBUG 1 */
/*
* Type of used arithmetics. SUSv3 requires us to have at least signed long.
*/
typedef long arith_t;
#define strtoarith_t(nptr, endptr, base) strtol(nptr, endptr, base)
#define atoarith_t(arg) strtol(arg, NULL, 0)
#define ARITH_FORMAT_STR "%ld"
typedef void *pointer;
#define STATIC static
#define MKINIT /* empty */