%{
       #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);
}