plan9fox/sys/src/cmd/bc.y
Ori Bernstein 5bc9b0c3ca improve usage messages (thanks henesy)
Fix inconsistencies between programs and their usage
messages,  correct instances where information seems
to be missing or lost. This  includes missing arguments,
making usage consistent with manuals, and so on.
2020-03-10 10:09:34 -07:00

990 lines
13 KiB
Plaintext

%{
#include <u.h>
#include <libc.h>
#include <bio.h>
#define bsp_max 5000
Biobuf *in;
Biobuf bstdin;
Biobuf bstdout;
char cary[1000];
char* cp = { cary };
char string[1000];
char* str = { string };
int crs = 128;
int rcrs = 128; /* reset crs */
int bindx = 0;
int lev = 0;
int ln;
char* ttp;
char* ss = "";
int bstack[10] = { 0 };
char* numb[15] =
{
" 0", " 1", " 2", " 3", " 4", " 5",
" 6", " 7", " 8", " 9", " 10", " 11",
" 12", " 13", " 14"
};
char* pre;
char* post;
long peekc = -1;
int sargc;
int ifile;
char** sargv;
char *funtab[] =
{
"<1>","<2>","<3>","<4>","<5>",
"<6>","<7>","<8>","<9>","<10>",
"<11>","<12>","<13>","<14>","<15>",
"<16>","<17>","<18>","<19>","<20>",
"<21>","<22>","<23>","<24>","<25>",
"<26>"
};
char *atab[] =
{
"<221>","<222>","<223>","<224>","<225>",
"<226>","<227>","<228>","<229>","<230>",
"<231>","<232>","<233>","<234>","<235>",
"<236>","<237>","<238>","<239>","<240>",
"<241>","<242>","<243>","<244>","<245>",
"<246>"
};
char* letr[26] =
{
"a","b","c","d","e","f","g","h","i","j",
"k","l","m","n","o","p","q","r","s","t",
"u","v","w","x","y","z"
};
char* dot = { "." };
char* bspace[bsp_max];
char** bsp_nxt = bspace;
int bdebug = 0;
int lflag;
int cflag;
int sflag;
char* bundle(int, ...);
void conout(char*, char*);
int cpeek(int, int, int);
int getch(void);
char* geta(char*);
char* getf(char*);
void getout(void);
void output(char*);
void pp(char*);
void routput(char*);
void tp(char*);
void yyerror(char*, ...);
int yyparse(void);
typedef void* pointer;
#pragma varargck type "lx" pointer
%}
%union
{
char* cptr;
int cc;
}
%type <cptr> pstat stat stat1 def slist dlets e ase nase
%type <cptr> slist re fprefix cargs eora cons constant lora
%type <cptr> crs
%token <cptr> LETTER EQOP _AUTO DOT
%token <cc> DIGIT SQRT LENGTH _IF FFF EQ
%token <cc> _PRINT _WHILE _FOR NE LE GE INCR DECR
%token <cc> _RETURN _BREAK _DEFINE BASE OBASE SCALE
%token <cc> QSTR ERROR
%right '=' EQOP
%left '+' '-'
%left '*' '/' '%'
%right '^'
%left UMINUS
%%
start:
start stuff
| stuff
stuff:
pstat tail
{
output($1);
}
| def dargs ')' '{' dlist slist '}'
{
ttp = bundle(6, pre, $6, post , "0", numb[lev], "Q");
conout(ttp, (char*)$1);
rcrs = crs;
output("");
lev = bindx = 0;
}
dlist:
tail
| dlist _AUTO dlets tail
stat:
stat1
| nase
{
if(sflag)
bundle(2, $1, "s.");
}
pstat:
stat1
{
if(sflag)
bundle(2, $1, "0");
}
| nase
{
if(!sflag)
bundle(2, $1, "ps.");
}
stat1:
{
bundle(1, "");
}
| ase
{
bundle(2, $1, "s.");
}
| SCALE '=' e
{
bundle(2, $3, "k");
}
| SCALE EQOP e
{
bundle(4, "K", $3, $2, "k");
}
| BASE '=' e
{
bundle(2, $3, "i");
}
| BASE EQOP e
{
bundle(4, "I", $3, $2, "i");
}
| OBASE '=' e
{
bundle(2, $3, "o");
}
| OBASE EQOP e
{
bundle(4, "O", $3, $2, "o");
}
| QSTR
{
bundle(3, "[", $1, "]P");
}
| _BREAK
{
bundle(2, numb[lev-bstack[bindx-1]], "Q");
}
| _PRINT e
{
bundle(2, $2, "ps.");
}
| _RETURN e
{
bundle(4, $2, post, numb[lev], "Q");
}
| _RETURN
{
bundle(4, "0", post, numb[lev], "Q");
}
| '{' slist '}'
{
$$ = $2;
}
| FFF
{
bundle(1, "fY");
}
| _IF crs BLEV '(' re ')' stat
{
conout($7, $2);
bundle(3, $5, $2, " ");
}
| _WHILE crs '(' re ')' stat BLEV
{
bundle(3, $6, $4, $2);
conout($$, $2);
bundle(3, $4, $2, " ");
}
| fprefix crs re ';' e ')' stat BLEV
{
bundle(5, $7, $5, "s.", $3, $2);
conout($$, $2);
bundle(5, $1, "s.", $3, $2, " ");
}
| '~' LETTER '=' e
{
bundle(3, $4, "S", $2);
}
fprefix:
_FOR '(' e ';'
{
$$ = $3;
}
BLEV:
=
{
--bindx;
}
slist:
stat
| slist tail stat
{
bundle(2, $1, $3);
}
tail:
'\n'
{
ln++;
}
| ';'
re:
e EQ e
{
$$ = bundle(3, $1, $3, "=");
}
| e '<' e
{
bundle(3, $1, $3, ">");
}
| e '>' e
{
bundle(3, $1, $3, "<");
}
| e NE e
{
bundle(3, $1, $3, "!=");
}
| e GE e
{
bundle(3, $1, $3, "!>");
}
| e LE e
{
bundle(3, $1, $3, "!<");
}
| e
{
bundle(2, $1, " 0!=");
}
nase:
'(' e ')'
{
$$ = $2;
}
| cons
{
bundle(3, " ", $1, " ");
}
| DOT cons
{
bundle(3, " .", $2, " ");
}
| cons DOT cons
{
bundle(5, " ", $1, ".", $3, " ");
}
| cons DOT
{
bundle(4, " ", $1, ".", " ");
}
| DOT
{
$<cptr>$ = "l.";
}
| LETTER '[' e ']'
{
bundle(3, $3, ";", geta($1));
}
| LETTER INCR
{
bundle(4, "l", $1, "d1+s", $1);
}
| INCR LETTER
{
bundle(4, "l", $2, "1+ds", $2);
}
| DECR LETTER
{
bundle(4, "l", $2, "1-ds", $2);
}
| LETTER DECR
{
bundle(4, "l", $1, "d1-s", $1);
}
| LETTER '[' e ']' INCR
{
bundle(7, $3, ";", geta($1), "d1+" ,$3, ":" ,geta($1));
}
| INCR LETTER '[' e ']'
{
bundle(7, $4, ";", geta($2), "1+d", $4, ":", geta($2));
}
| LETTER '[' e ']' DECR
{
bundle(7, $3, ";", geta($1), "d1-", $3, ":", geta($1));
}
| DECR LETTER '[' e ']'
{
bundle(7, $4, ";", geta($2), "1-d", $4, ":" ,geta($2));
}
| SCALE INCR
{
bundle(1, "Kd1+k");
}
| INCR SCALE
{
bundle(1, "K1+dk");
}
| SCALE DECR
{
bundle(1, "Kd1-k");
}
| DECR SCALE
{
bundle(1, "K1-dk");
}
| BASE INCR
{
bundle(1, "Id1+i");
}
| INCR BASE
{
bundle(1, "I1+di");
}
| BASE DECR
{
bundle(1, "Id1-i");
}
| DECR BASE
{
bundle(1, "I1-di");
}
| OBASE INCR
{
bundle(1, "Od1+o");
}
| INCR OBASE
{
bundle(1, "O1+do");
}
| OBASE DECR
{
bundle(1, "Od1-o");
}
| DECR OBASE
{
bundle(1, "O1-do");
}
| LETTER '(' cargs ')'
{
bundle(4, $3, "l", getf($1), "x");
}
| LETTER '(' ')'
{
bundle(3, "l", getf($1), "x");
}
| LETTER = {
bundle(2, "l", $1);
}
| LENGTH '(' e ')'
{
bundle(2, $3, "Z");
}
| SCALE '(' e ')'
{
bundle(2, $3, "X");
}
| '?'
{
bundle(1, "?");
}
| SQRT '(' e ')'
{
bundle(2, $3, "v");
}
| '~' LETTER
{
bundle(2, "L", $2);
}
| SCALE
{
bundle(1, "K");
}
| BASE
{
bundle(1, "I");
}
| OBASE
{
bundle(1, "O");
}
| '-' e
{
bundle(3, " 0", $2, "-");
}
| e '+' e
{
bundle(3, $1, $3, "+");
}
| e '-' e
{
bundle(3, $1, $3, "-");
}
| e '*' e
{
bundle(3, $1, $3, "*");
}
| e '/' e
{
bundle(3, $1, $3, "/");
}
| e '%' e
{
bundle(3, $1, $3, "%%");
}
| e '^' e
{
bundle(3, $1, $3, "^");
}
ase:
LETTER '=' e
{
bundle(3, $3, "ds", $1);
}
| LETTER '[' e ']' '=' e
{
bundle(5, $6, "d", $3, ":", geta($1));
}
| LETTER EQOP e
{
bundle(6, "l", $1, $3, $2, "ds", $1);
}
| LETTER '[' e ']' EQOP e
{
bundle(9, $3, ";", geta($1), $6, $5, "d", $3, ":", geta($1));
}
e:
ase
| nase
cargs:
eora
| cargs ',' eora
{
bundle(2, $1, $3);
}
eora:
e
| LETTER '[' ']'
{
bundle(2, "l", geta($1));
}
cons:
constant
{
*cp++ = 0;
}
constant:
'_'
{
$<cptr>$ = cp;
*cp++ = '_';
}
| DIGIT
{
$<cptr>$ = cp;
*cp++ = $1;
}
| constant DIGIT
{
*cp++ = $2;
}
crs:
=
{
$$ = cp;
*cp++ = '<';
*cp++ = crs/100+'0';
*cp++ = (crs%100)/10+'0';
*cp++ = crs%10+'0';
*cp++ = '>';
*cp++ = '\0';
if(crs++ >= 220) {
yyerror("program too big");
getout();
}
bstack[bindx++] = lev++;
}
def:
_DEFINE LETTER '('
{
$$ = getf($2);
pre = (char*)"";
post = (char*)"";
lev = 1;
bindx = 0;
bstack[bindx] = 0;
}
dargs:
| lora
{
pp((char*)$1);
}
| dargs ',' lora
{
pp((char*)$3);
}
dlets:
lora
{
tp((char*)$1);
}
| dlets ',' lora
{
tp((char*)$3);
}
lora:
LETTER
{
$<cptr>$=$1;
}
| LETTER '[' ']'
{
$$ = geta($1);
}
%%
int
yylex(void)
{
int c, ch;
restart:
c = getch();
peekc = -1;
while(c == ' ' || c == '\t')
c = getch();
if(c == '\\') {
getch();
goto restart;
}
if(c >= 'a' && c <= 'z') {
/* look ahead to look for reserved words */
peekc = getch();
if(peekc >= 'a' && peekc <= 'z') { /* must be reserved word */
if(c=='p' && peekc=='r') {
c = _PRINT;
goto skip;
}
if(c=='i' && peekc=='f') {
c = _IF;
goto skip;
}
if(c=='w' && peekc=='h') {
c = _WHILE;
goto skip;
}
if(c=='f' && peekc=='o') {
c = _FOR;
goto skip;
}
if(c=='s' && peekc=='q') {
c = SQRT;
goto skip;
}
if(c=='r' && peekc=='e') {
c = _RETURN;
goto skip;
}
if(c=='b' && peekc=='r') {
c = _BREAK;
goto skip;
}
if(c=='d' && peekc=='e') {
c = _DEFINE;
goto skip;
}
if(c=='s' && peekc=='c') {
c = SCALE;
goto skip;
}
if(c=='b' && peekc=='a') {
c = BASE;
goto skip;
}
if(c=='i' && peekc=='b') {
c = BASE;
goto skip;
}
if(c=='o' && peekc=='b') {
c = OBASE;
goto skip;
}
if(c=='d' && peekc=='i') {
c = FFF;
goto skip;
}
if(c=='a' && peekc=='u') {
c = _AUTO;
goto skip;
}
if(c=='l' && peekc=='e') {
c = LENGTH;
goto skip;
}
if(c=='q' && peekc=='u')
getout();
/* could not be found */
return ERROR;
skip: /* skip over rest of word */
peekc = -1;
for(;;) {
ch = getch();
if(ch < 'a' || ch > 'z')
break;
}
peekc = ch;
return c;
}
/* usual case; just one single letter */
yylval.cptr = letr[c-'a'];
return LETTER;
}
if((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) {
yylval.cc = c;
return DIGIT;
}
switch(c) {
case '.':
return DOT;
case '*':
yylval.cptr = "*";
return cpeek('=', EQOP, c);
case '%':
yylval.cptr = "%%";
return cpeek('=', EQOP, c);
case '^':
yylval.cptr = "^";
return cpeek('=', EQOP, c);
case '+':
ch = cpeek('=', EQOP, c);
if(ch == EQOP) {
yylval.cptr = "+";
return ch;
}
return cpeek('+', INCR, c);
case '-':
ch = cpeek('=', EQOP, c);
if(ch == EQOP) {
yylval.cptr = "-";
return ch;
}
return cpeek('-', DECR, c);
case '=':
return cpeek('=', EQ, '=');
case '<':
return cpeek('=', LE, '<');
case '>':
return cpeek('=', GE, '>');
case '!':
return cpeek('=', NE, '!');
case '/':
ch = cpeek('=', EQOP, c);
if(ch == EQOP) {
yylval.cptr = "/";
return ch;
}
if(peekc == '*') {
peekc = -1;
for(;;) {
ch = getch();
if(ch == '*') {
peekc = getch();
if(peekc == '/') {
peekc = -1;
goto restart;
}
}
}
}
return c;
case '"':
yylval.cptr = str;
while((c=getch()) != '"'){
*str++ = c;
if(str >= &string[999]){
yyerror("string space exceeded");
getout();
}
}
*str++ = 0;
return QSTR;
default:
return c;
}
}
int
cpeek(int c, int yes, int no)
{
peekc = getch();
if(peekc == c) {
peekc = -1;
return yes;
}
return no;
}
int
getch(void)
{
long ch;
loop:
ch = peekc;
if(ch < 0){
if(in == 0)
ch = -1;
else
ch = Bgetc(in);
}
peekc = -1;
if(ch >= 0)
return ch;
ifile++;
if(ifile > sargc) {
if(ifile >= sargc+2)
getout();
in = &bstdin;
Binit(in, 0, OREAD);
ln = 0;
goto loop;
}
if(in)
Bterm(in);
if((in = Bopen(sargv[ifile], OREAD)) != 0){
ln = 0;
ss = sargv[ifile];
goto loop;
}
yyerror("cannot open input file");
return 0; /* shut up ken */
}
char*
bundle(int a, ...)
{
int i;
char **q;
va_list arg;
i = a;
va_start(arg, a);
q = bsp_nxt;
if(bdebug)
fprint(2, "bundle %d elements at %lx\n", i, q);
while(i-- > 0) {
if(bsp_nxt >= &bspace[bsp_max])
yyerror("bundling space exceeded");
*bsp_nxt++ = va_arg(arg, char*);
}
*bsp_nxt++ = 0;
va_end(arg);
yyval.cptr = (char*)q;
return (char*)q;
}
void
routput(char *p)
{
char **pp;
if(bdebug)
fprint(2, "routput(%lx)\n", p);
if((char**)p >= &bspace[0] && (char**)p < &bspace[bsp_max]) {
/* part of a bundle */
pp = (char**)p;
while(*pp != 0)
routput(*pp++);
} else
Bprint(&bstdout, p); /* character string */
}
void
output(char *p)
{
routput(p);
bsp_nxt = &bspace[0];
Bprint(&bstdout, "\n");
Bflush(&bstdout);
cp = cary;
crs = rcrs;
}
void
conout(char *p, char *s)
{
Bprint(&bstdout, "[");
routput(p);
Bprint(&bstdout, "]s%s\n", s);
Bflush(&bstdout);
lev--;
}
void
yyerror(char *s, ...)
{
if(ifile > sargc)
ss = "stdin";
Bprint(&bstdout, "c[%s:%d %s]pc\n", ss, ln+1, s);
Bflush(&bstdout);
cp = cary;
crs = rcrs;
bindx = 0;
lev = 0;
bsp_nxt = &bspace[0];
}
void
pp(char *s)
{
/* puts the relevant stuff on pre and post for the letter s */
bundle(3, "S", s, pre);
pre = yyval.cptr;
bundle(4, post, "L", s, "s.");
post = yyval.cptr;
}
void
tp(char *s)
{
/* same as pp, but for temps */
bundle(3, "0S", s, pre);
pre = yyval.cptr;
bundle(4, post, "L", s, "s.");
post = yyval.cptr;
}
void
yyinit(int argc, char **argv)
{
Binit(&bstdout, 1, OWRITE);
sargv = argv;
sargc = argc - 1;
if(sargc == 0) {
in = &bstdin;
Binit(in, 0, OREAD);
} else if((in = Bopen(sargv[1], OREAD)) == 0)
yyerror("cannot open input file");
ifile = 1;
ln = 0;
ss = sargv[1];
}
void
getout(void)
{
Bprint(&bstdout, "q");
Bflush(&bstdout);
exits(0);
}
char*
getf(char *p)
{
return funtab[*p - 'a'];
}
char*
geta(char *p)
{
return atab[*p - 'a'];
}
void
main(int argc, char **argv)
{
int p[2];
while(argc > 1 && *argv[1] == '-') {
switch(argv[1][1]) {
case 'd':
bdebug++;
break;
case 'c':
cflag++;
break;
case 'l':
lflag++;
break;
case 's':
sflag++;
break;
default:
fprint(2, "usage: bc [-cdls] [file ...]\n");
exits("usage");
}
argc--;
argv++;
}
if(lflag) {
argv--;
argc++;
argv[1] = "/sys/lib/bclib";
}
if(cflag) {
yyinit(argc, argv);
for(;;)
yyparse();
/* not reached */
}
pipe(p);
if(fork() == 0) {
dup(p[1], 1);
close(p[0]);
close(p[1]);
yyinit(argc, argv);
for(;;)
yyparse();
}
dup(p[0], 0);
close(p[0]);
close(p[1]);
execl("/bin/dc", "dc", nil);
}