test: fix expression parser

The old parser code was rubbish and only worked for trivial
expressions.  The new code properly handles complex expressions,
including short circuit evaluation.

As such, the BUGS section has been removed from the test(1) man page.
The description of an unimplemented feature has also been removed.
This commit is contained in:
Alex Musolino 2021-02-06 15:51:09 +10:30
parent 4ec97f73ee
commit b562b269ce
2 changed files with 128 additions and 87 deletions

View file

@ -100,11 +100,6 @@ or
.BR -le .BR -le
may be used in place of may be used in place of
.BR -eq . .BR -eq .
The (nonstandard) construct
.BI -l " string\f1,
meaning the length of
.IR string ,
may be used in place of an integer.
.TP .TP
.IB a " -nt " b .IB a " -nt " b
True if file True if file
@ -209,10 +204,3 @@ is in the current directory.
.B /sys/src/cmd/test.c .B /sys/src/cmd/test.c
.SH "SEE ALSO" .SH "SEE ALSO"
.IR rc (1) .IR rc (1)
.SH BUGS
Won't complain about extraneous arguments
since there may be arguments left unprocessed by
short-circuit evaluation of
.B -a
or
.BR -o .

View file

@ -30,7 +30,7 @@ int isolderthan(char *, char *);
int isnewerthan(char *, char *); int isnewerthan(char *, char *);
int hasmode(char *, ulong); int hasmode(char *, ulong);
int tio(char *, int); int tio(char *, int);
int e(void), e1(void), e2(void), e3(void); int e(int), e1(int), e2(int), e3(int);
char *nxtarg(int); char *nxtarg(int);
void void
@ -47,12 +47,8 @@ main(int argc, char *argv[])
argv[ac] = 0; argv[ac] = 0;
if (ac<=1) if (ac<=1)
exits("usage"); exits("usage");
r = e(); r = e(1);
/* if((c = nxtarg(1)) != nil)
* nice idea but short-circuit -o and -a operators may have
* not consumed their right-hand sides.
*/
if(0 && (c = nxtarg(1)) != nil)
synbad("unexpected operator/operand: ", c); synbad("unexpected operator/operand: ", c);
exits(r?0:"false"); exits(r?0:"false");
} }
@ -81,78 +77,128 @@ nxtintarg(int *pans)
} }
int int
e(void) e(int eval)
{ {
char *op;
int p1; int p1;
p1 = e1(); p1 = e1(eval);
if (EQ(nxtarg(1), "-o")) for(;;){
return(p1 || e()); op = nxtarg(1);
if(op == nil)
break;
if(!EQ(op, "-o")){
ap--; ap--;
return(p1); return p1;
}
if(!p1 && eval)
p1 |= e1(1);
else
e1(0);
}
return p1;
} }
int int
e1(void) e1(int eval)
{ {
char *op;
int p1; int p1;
p1 = e2(); p1 = e2(eval);
if (EQ(nxtarg(1), "-a")) for(;;){
return (p1 && e1()); op = nxtarg(1);
if(op == nil)
break;
if(!EQ(op, "-a")){
ap--; ap--;
return(p1); return p1;
}
if(p1 && eval)
p1 &= e2(1);
else
e2(0);
}
return p1;
} }
int int
e2(void) e2(int eval)
{ {
if (EQ(nxtarg(0), "!")) char *op;
return(!e2()); int p1;
p1 = 0;
for(;;){
op = nxtarg(1);
if(op == nil)
return p1 ^ 1;
if(!EQ(op, "!"))
break;
p1 ^= 1;
}
ap--; ap--;
return(e3()); return(p1^e3(eval));
} }
int int
e3(void) e3(int eval)
{ {
int p1, int1, int2; int p1, int1, int2;
char *a, *p2; char *a, *b, *p2;
a = nxtarg(0); a = nxtarg(0);
if(EQ(a, "(")) { if(EQ(a, "(")) {
p1 = e(); p1 = e(eval);
if(!EQ(nxtarg(0), ")")) if(!EQ(nxtarg(0), ")"))
synbad(") expected",""); synbad(") expected","");
return(p1); return(p1);
} }
if(EQ(a, "-A")) if(EQ(a, "-A")){
return(hasmode(nxtarg(0), DMAPPEND)); b = nxtarg(0);
return(eval && hasmode(b, DMAPPEND));
}
if(EQ(a, "-L")) if(EQ(a, "-L")){
return(hasmode(nxtarg(0), DMEXCL)); b = nxtarg(0);
return(eval && hasmode(b, DMEXCL));
}
if(EQ(a, "-T")) if(EQ(a, "-T")){
return(hasmode(nxtarg(0), DMTMP)); b = nxtarg(0);
return(eval && hasmode(b, DMTMP));
}
if(EQ(a, "-f")) if(EQ(a, "-f")){
return(isreg(nxtarg(0))); b = nxtarg(0);
return(eval && isreg(b));
}
if(EQ(a, "-d")) if(EQ(a, "-d")){
return(isdir(nxtarg(0))); b = nxtarg(0);
return(eval && isdir(b));
}
if(EQ(a, "-r")) if(EQ(a, "-r")){
return(tio(nxtarg(0), AREAD)); b = nxtarg(0);
return(eval && tio(b, AREAD));
}
if(EQ(a, "-w")) if(EQ(a, "-w")){
return(tio(nxtarg(0), AWRITE)); b = nxtarg(0);
return(eval && tio(b, AWRITE));
}
if(EQ(a, "-x")) if(EQ(a, "-x")){
return(tio(nxtarg(0), AEXEC)); b = nxtarg(0);
return(eval && tio(b, AEXEC));
}
if(EQ(a, "-e")) if(EQ(a, "-e")){
return(tio(nxtarg(0), AEXIST)); b = nxtarg(0);
return(eval && tio(b, AEXIST));
}
if(EQ(a, "-c")) if(EQ(a, "-c"))
return(0); return(0);
@ -166,14 +212,16 @@ e3(void)
if(EQ(a, "-g")) if(EQ(a, "-g"))
return(0); return(0);
if(EQ(a, "-s")) if(EQ(a, "-s")){
return(fsizep(nxtarg(0))); b = nxtarg(0);
return(eval && fsizep(b));
}
if(EQ(a, "-t")) if(EQ(a, "-t"))
if(ap>=ac) if(ap>=ac)
return(isatty(1)); return(eval && isatty(1));
else if(nxtintarg(&int1)) else if(nxtintarg(&int1))
return(isatty(int1)); return(eval && isatty(int1));
else else
synbad("not a valid file descriptor number ", ""); synbad("not a valid file descriptor number ", "");
@ -191,18 +239,22 @@ e3(void)
if(EQ(p2, "!=")) if(EQ(p2, "!="))
return(!EQ(nxtarg(0), a)); return(!EQ(nxtarg(0), a));
if(EQ(p2, "-older")) if(EQ(p2, "-older")){
return(isolder(nxtarg(0), a)); b = nxtarg(0);
return(eval && isolder(b, a));
}
if(EQ(p2, "-ot")) if(EQ(p2, "-ot")){
return(isolderthan(nxtarg(0), a)); b = nxtarg(0);
return(eval && isolderthan(b, a));
}
if(EQ(p2, "-nt")) if(EQ(p2, "-nt")){
return(isnewerthan(nxtarg(0), a)); b = nxtarg(0);
return(eval && isnewerthan(b, a));
if(!isint(a, &int1)) }
synbad("unexpected operator/operand: ", p2);
if(isint(a, &int1)){
if(nxtintarg(&int2)){ if(nxtintarg(&int2)){
if(EQ(p2, "-eq")) if(EQ(p2, "-eq"))
return(int1==int2); return(int1==int2);
@ -216,10 +268,11 @@ e3(void)
return(int1>=int2); return(int1>=int2);
if(EQ(p2, "-le")) if(EQ(p2, "-le"))
return(int1<=int2); return(int1<=int2);
ap--;
} }
}
synbad("unknown operator ",p2); ap--;
return 0; /* to shut ken up */ return !EQ(a, "");
} }
int int