URI:
       bc.y - sbase - suckless unix tools
  HTML git clone git://git.suckless.org/sbase
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       bc.y (17788B)
       ---
            1 %{
            2 #include <libgen.h>
            3 #include <unistd.h>
            4 
            5 #include <assert.h>
            6 #include <ctype.h>
            7 #include <errno.h>
            8 #include <setjmp.h>
            9 #include <stdarg.h>
           10 #include <stdio.h>
           11 #include <stdlib.h>
           12 #include <string.h>
           13 
           14 #include "arg.h"
           15 #include "util.h"
           16 
           17 #define DIGITS   "0123456789ABCDEF"
           18 #define NESTED_MAX 32
           19 
           20 #define funid(f) ((f)[0] - 'a' + 1)
           21 
           22 int yydebug;
           23 
           24 typedef struct macro Macro;
           25 
           26 struct macro {
           27         int op;
           28         int id;
           29         char *name;
           30         int flowid;
           31         int nested;
           32 };
           33 
           34 static int yyerror(char *);
           35 static int yylex(void);
           36 
           37 static void quit(void);
           38 static char *code(char *, ...);
           39 static char *forcode(Macro *, char *, char *, char *, char *);
           40 static char *whilecode(Macro *, char *, char *);
           41 static char *ifcode(Macro *, char *, char *);
           42 static char *funcode(Macro *, char *, char *, char *);
           43 static char *param(char *, char *), *local(char *, char *);
           44 static Macro *define(char *, char *);
           45 static char *retcode(char *);
           46 static char *brkcode(void);
           47 static Macro *macro(int);
           48 
           49 static char *ftn(char *);
           50 static char *var(char *);
           51 static char *ary(char *);
           52 static void writeout(char *);
           53 
           54 static char *yytext, *buff, *unwind;
           55 static char *filename;
           56 static FILE *filep;
           57 static int lineno, nerr, flowid;
           58 static jmp_buf recover;
           59 static int nested, inhome;
           60 static Macro macros[NESTED_MAX];
           61 int cflag, dflag, lflag, sflag;
           62 
           63 static char *dcprog = "dc";
           64 
           65 %}
           66 
           67 %union {
           68         char *str;
           69         char id[2];
           70         Macro *macro;
           71 }
           72 
           73 %token <id> ID
           74 %token <str> STRING NUMBER
           75 %token <str> EQOP '+' '-' '*' '/' '%' '^' INCDEC
           76 %token HOME LOOP
           77 %token DOT
           78 %token EQ
           79 %token LE
           80 %token GE
           81 %token NE
           82 %token DEF
           83 %token BREAK
           84 %token QUIT
           85 %token LENGTH
           86 %token RETURN
           87 %token FOR
           88 %token IF
           89 %token WHILE
           90 %token SQRT
           91 %token SCALE
           92 %token IBASE
           93 %token OBASE
           94 %token AUTO PARAM
           95 %token PRINT
           96 
           97 %type <str> item statlst scolonlst
           98 %type <str> function assign nexpr expr exprstat rel stat ary cond
           99 %type <str> autolst arglst parlst
          100 %type <str> params param locals local
          101 %type <macro> def if for while
          102 
          103 %right        '=' EQOP
          104 %left        '+' '-'
          105 %left        '*' '/' '%'
          106 %right        '^'
          107 
          108 %start    program
          109 
          110 %%
          111 
          112 program  :
          113          | item program
          114          ;
          115 
          116 item     : scolonlst '\n'       {writeout($1);}
          117          | function             {writeout($1);}
          118          ;
          119 
          120 function : def parlst '{' '\n' autolst statlst '}' {$$ = funcode($1, $2, $5, $6);}
          121          ;
          122 
          123 scolonlst:                      {$$ = code("");}
          124          | stat
          125          | scolonlst ';' stat   {$$ = code("%s%s", $1, $3);}
          126          | scolonlst ';'
          127          ;
          128 
          129 statlst :                       {$$ = code("");}
          130         | stat
          131         | statlst '\n' stat     {$$ = code("%s%s", $1, $3);}
          132         | statlst ';' stat      {$$ = code("%s%s", $1, $3);}
          133         | statlst '\n'
          134         | statlst ';'
          135         ;
          136 
          137 stat    : exprstat
          138         | PRINT expr            {$$ = code("%sps.", $2);}
          139         | PRINT STRING          {$$ = code("[%s]P", $2);}
          140         | PRINT STRING ',' expr {$$ = code("[%s]P%sps.", $2, $4);}
          141         | STRING                {$$ = code("[%s]P", $1);}
          142         | BREAK                 {$$ = brkcode();}
          143         | QUIT                  {quit();}
          144         | RETURN                {$$ = retcode(code(" 0"));}
          145         | RETURN '(' expr ')'   {$$ = retcode($3);}
          146         | RETURN '(' ')'        {$$ = retcode(code(" 0"));}
          147         | while cond stat       {$$ = whilecode($1, $2, $3);}
          148         | if cond stat          {$$ = ifcode($1, $2, $3);}
          149         | '{' statlst '}'       {$$ = $2;}
          150         | for '(' expr ';' rel ';' expr ')' stat  {$$ = forcode($1, $3, $5, $7, $9);}
          151         ;
          152 
          153 while   : WHILE                 {$$ = macro(LOOP);}
          154         ;
          155 
          156 if      : IF                    {$$ = macro(IF);}
          157         ;
          158 
          159 for     : FOR                   {$$ = macro(LOOP);}
          160         ;
          161 
          162 def     : DEF ID                {$$ = macro(DEF);}
          163         ;
          164 
          165 parlst  : '(' ')'               {$$ = code("");}
          166         | '(' params ')'        {$$ = $2;}
          167         ;
          168 
          169 params  : param                 {$$ = param(NULL, $1);}
          170         | params ',' param      {$$ = param($1, $3);}
          171         ;
          172 
          173 param   : ID                    {$$ = var($1);}
          174         | ID '[' ']'            {$$ = ary($1);}
          175         ;
          176 
          177 autolst :                       {$$ = code("");}
          178         | AUTO locals '\n'      {$$ = $2;}
          179         | AUTO locals ';'       {$$ = $2;}
          180         ;
          181 
          182 locals  : local                 {$$ = local(NULL, $1);}
          183         | locals ',' local      {$$ = local($1, $3);}
          184         ;
          185 
          186 local   : ID                    {$$ = var($1);}
          187         | ID '[' ']'            {$$ = ary($1);}
          188         ;
          189 
          190 arglst  : expr
          191         | ID '[' ']'            {$$ = code("%s", ary($1));}
          192         | expr ',' arglst       {$$ = code("%s%s", $1, $3);}
          193         | ID '[' ']' ',' arglst {$$ = code("%s%s", ary($1), $5);}
          194         ;
          195 
          196 cond    : '(' rel ')'           {$$ = $2;}
          197         ;
          198 
          199 rel     : expr                  {$$ = code("%s 0!=", $1);}
          200         | expr EQ expr          {$$ = code("%s%s=", $1, $3);}
          201         | expr LE expr          {$$ = code("%s%s!<", $1, $3);}
          202         | expr GE expr          {$$ = code("%s%s!>", $1, $3);}
          203         | expr NE expr          {$$ = code("%s%s!=", $1, $3);}
          204         | expr '<' expr         {$$ = code("%s%s>", $1, $3);}
          205         | expr '>' expr         {$$ = code("%s%s<", $1, $3);}
          206         ;
          207 
          208 exprstat: nexpr                 {$$ = code("%s%ss.", $1, code(sflag ? "" : "p"));}
          209         | assign                {$$ = code("%ss.", $1);}
          210         ;
          211 
          212 expr    : nexpr
          213         | assign
          214         ;
          215 
          216 nexpr   : NUMBER                {$$ = code(" %s", code($1));}
          217         | ID                    {$$ = code("l%s", var($1));}
          218         | DOT                   {$$ = code("l.");}
          219         | SCALE                 {$$ = code("K");}
          220         | IBASE                 {$$ = code("I");}
          221         | OBASE                 {$$ = code("O");}
          222         | ID ary                {$$ = code("%s;%s", $2, ary($1));}
          223         | '(' expr ')'          {$$ = $2;}
          224         | ID '(' arglst ')'     {$$ = code("%sl%sx", $3, ftn($1));}
          225         | ID '(' ')'            {$$ = code("l%sx", ftn($1));}
          226         | '-' expr              {$$ = code("0%s-", $2);}
          227         | expr '+' expr         {$$ = code("%s%s+", $1, $3);}
          228         | expr '-' expr         {$$ = code("%s%s-", $1, $3);}
          229         | expr '*' expr         {$$ = code("%s%s*", $1, $3);}
          230         | expr '/' expr         {$$ = code("%s%s/", $1, $3);}
          231         | expr '%' expr         {$$ = code("%s%s%%", $1, $3);}
          232         | expr '^' expr         {$$ = code("%s%s^", $1, $3);}
          233         | LENGTH '(' expr ')'   {$$ = code("%sZ", $3);}
          234         | SQRT '(' expr ')'     {$$ = code("%sv", $3);}
          235         | SCALE '(' expr ')'    {$$ = code("%sX", $3);}
          236         | INCDEC ID             {$$ = code("l%s1%sds%s", var($2), code($1), var($2));}
          237         | INCDEC SCALE          {$$ = code("K1%sk", code($1));}
          238         | INCDEC IBASE          {$$ = code("I1%sdi", code($1));}
          239         | INCDEC OBASE          {$$ = code("O1%sdo", code($1));}
          240         | INCDEC ID ary         {$$ = code("%sdS_;%s1%sdL_:%s", $3, ary($2), code($1), ary($2));}
          241         | ID INCDEC             {$$ = code("l%sd1%ss%s", var($1), code($2), var($1));}
          242         | SCALE INCDEC          {$$ = code("Kd1%sk", code($2));}
          243         | IBASE INCDEC          {$$ = code("Id1%si", code($2));}
          244         | OBASE INCDEC          {$$ = code("Od1%so", code($2));}
          245         | ID ary INCDEC         {$$ = code("%sds.;%sd1%sl.:%s", $2, ary($1), code($3), ary($1));}
          246         ;
          247 
          248 assign  : ID '=' expr           {$$ = code("%sds%s", $3, var($1));}
          249         | SCALE '=' expr        {$$ = code("%sdk", $3);}
          250         | IBASE '=' expr        {$$ = code("%sdi", $3);}
          251         | OBASE '=' expr        {$$ = code("%sdo", $3);}
          252         | ID ary '=' expr       {$$ = code("%sd%s:%s", $4, $2, ary($1));}
          253         | ID EQOP expr          {$$ = code("%sl%s%sds%s", $3, var($1), code($2), var($1));}
          254         | SCALE EQOP expr       {$$ = code("%sK%sdk", $3, code($2));}
          255         | IBASE EQOP expr       {$$ = code("%sI%sdi", $3, code($2));}
          256         | OBASE EQOP expr       {$$ = code("%sO%sdo", $3, code($2));}
          257         | ID ary EQOP expr      {$$ = code("%s%sds.;%s%sdl.:s", $4, $2, ary($1), code($3), ary($1));}
          258         ;
          259 
          260 ary     : '[' expr ']'          {$$ = $2;}
          261         ;
          262 
          263 %%
          264 static int
          265 yyerror(char *s)
          266 {
          267         fprintf(stderr, "bc: %s:%d: %s\n", filename, lineno, s);
          268         nerr++;
          269         longjmp(recover, 1);
          270 }
          271 
          272 static void
          273 writeout(char *s)
          274 {
          275         if (write(1, s, strlen(s)) < 0)
          276                 goto err;
          277         if (write(1, "\n", 1) < 0)
          278                 goto err;
          279         free(s);
          280         return;
          281         
          282 err:
          283         eprintf("writing to dc:");
          284 }
          285 
          286 static char *
          287 code(char *fmt, ...)
          288 {
          289         char *s, *t;
          290         va_list ap;
          291         int c, len, room;
          292 
          293         va_start(ap, fmt);
          294         room = BUFSIZ;
          295         for (s = buff; *fmt; s += len) {
          296                 len = 1;
          297                 if ((c = *fmt++) != '%')
          298                         goto append;
          299 
          300                 switch (*fmt++) {
          301                 case 'd':
          302                         c = va_arg(ap, int);
          303                         len = snprintf(s, room, "%d", c);
          304                         if (len < 0 || len >= room)
          305                                 goto err;
          306                         break;
          307                 case 'c':
          308                         c = va_arg(ap, int);
          309                         goto append;
          310                 case 's':
          311                         t = va_arg(ap, void *);
          312                         len = strlen(t);
          313                         if (len >= room)
          314                                 goto err;
          315                         memcpy(s, t, len);
          316                         free(t);
          317                         break;
          318                 case '%':
          319                 append:
          320                         if (room <= 1)
          321                                 goto err;
          322                         *s = c;
          323                         break;
          324                 default:
          325                         abort();
          326                 }
          327 
          328                 room -= len;
          329         }
          330         va_end(ap);
          331 
          332         *s = '\0';
          333         return estrdup(buff);
          334 
          335 err:
          336         eprintf("unable to code requested operation\n");
          337         return NULL;
          338 }
          339 
          340 static Macro *
          341 macro(int op)
          342 {
          343         int preop;
          344         Macro *d, *p;
          345 
          346         if (nested == NESTED_MAX)
          347                 yyerror("too much nesting");
          348 
          349         d = &macros[nested];
          350         d->op = op;
          351         d->nested = nested++;
          352         d->name = NULL;
          353 
          354         switch (op) {
          355         case HOME:
          356                 d->id = 0;
          357                 d->flowid = flowid;
          358                 inhome = 1;
          359                 break;
          360         case DEF:
          361                 unwind = estrdup("");
          362                 inhome = 0;
          363                 d->id = funid(yytext);
          364                 d->name = estrdup(yytext);
          365                 d->flowid = macros[0].flowid;
          366                 break;
          367         default:
          368                 assert(nested > 1);
          369                 preop = d[-1].op;
          370                 d->flowid = d[-1].flowid;
          371                 if (preop != HOME && preop != DEF) {
          372                         if (d->flowid == 255)
          373                                 eprintf("too many control flow structures");
          374                         d->flowid++;
          375                 }
          376                 d->id = d->flowid;
          377                 if (!inhome) {
          378                         /* populate reserved id */
          379                         flowid = d->flowid;
          380                         for (p = d; p != macros; --p)
          381                                 p[-1].flowid++;
          382                 }
          383                 break;
          384         }
          385 
          386         return d;
          387 }
          388 
          389 static char *
          390 decl(int type, char *list, char *id)
          391 {
          392         char *i1, *i2;
          393 
          394         i1 = estrdup(id);
          395         i2 = estrdup(id);
          396         free(id);
          397 
          398         if (!list)
          399                 list = estrdup("");
          400 
          401         unwind = code("%sL%ss.", unwind, i1);
          402 
          403         return code((type == AUTO) ? "0S%s%s" : "S%s%s", i2, list);
          404 }
          405 
          406 static char *
          407 param(char *list, char *id)
          408 {
          409         return decl(PARAM, list, id);
          410 }
          411 
          412 static char *
          413 local(char *list, char *id)
          414 {
          415         return decl(AUTO, list, id);
          416 }
          417 
          418 static char *
          419 funcode(Macro *d, char *params, char *vars, char *body)
          420 {
          421         char *s;
          422 
          423         if (strlen(d->name) > 1) {
          424                 s = code("[%s%s%s%s]s\"()%s\"",
          425                          vars, params,
          426                          body,
          427                          retcode(code(" 0")),
          428                          d->name);
          429         } else {
          430                 s = code(sflag ? "[%s%s%s%s]s<%d>" : "[%s%s%s%s]s%c",
          431                          vars, params,
          432                          body,
          433                          retcode(code(" 0")),
          434                          d->id);
          435                 free(d->name);
          436         }
          437 
          438         free(unwind);
          439         unwind = NULL;
          440         nested--;
          441         inhome = 0;
          442 
          443         return s;
          444 }
          445 
          446 static char *
          447 brkcode(void)
          448 {
          449         Macro *d;
          450 
          451         for (d = &macros[nested-1]; d->op != HOME && d->op != LOOP; --d)
          452                 ;
          453         if (d->op == HOME)
          454                 yyerror("break not in for or while");
          455         return code(" %dQ", nested  - d->nested);
          456 }
          457 
          458 static char *
          459 forcode(Macro *d, char *init, char *cmp, char *inc, char *body)
          460 {
          461         char *s;
          462 
          463         s = code(sflag ? "[%s%ss.%s<%d>]s<%d>" : "[%s%ss.%s%c]s%c",
          464                  body,
          465                  inc,
          466                  estrdup(cmp),
          467                  d->id, d->id);
          468         writeout(s);
          469 
          470         s = code(sflag ? "%ss.%s<%d> " : "%ss.%s%c ",
          471                  init,
          472                  cmp,
          473                  d->id);
          474         nested--;
          475 
          476         return s;
          477 }
          478 
          479 static char *
          480 whilecode(Macro *d, char *cmp, char *body)
          481 {
          482         char *s;
          483 
          484         s = code(sflag ? "[%s%s<%d>]s<%d>" : "[%s%s%c]s%c",
          485                  body,
          486                  estrdup(cmp),
          487                  d->id, d->id);
          488         writeout(s);
          489 
          490         s = code(sflag ? "%s<%d> " : "%s%c ",
          491                  cmp, d->id);
          492         nested--;
          493 
          494         return s;
          495 }
          496 
          497 static char *
          498 ifcode(Macro *d, char *cmp, char *body)
          499 {
          500         char *s;
          501 
          502         s = code(sflag ? "[%s]s<%d>" : "[%s]s%c",
          503                  body, d->id);
          504         writeout(s);
          505 
          506         s = code(sflag ? "%s<%d> " : "%s%c ",
          507                  cmp, d->id);
          508         nested--;
          509 
          510         return s;
          511 }
          512 
          513 static char *
          514 retcode(char *expr)
          515 {
          516         char *s;
          517 
          518         if (nested < 2 || macros[1].op != DEF)
          519                 yyerror("return must be in a function");
          520         return code("%s %s %dQ", expr, estrdup(unwind), nested - 1);
          521 }
          522 
          523 static char *
          524 ary(char *s)
          525 {
          526         if (strlen(s) == 1)
          527                 return code("%c", toupper(s[0]));
          528         return code("\"[]%s\"", estrdup(s));
          529 }
          530 
          531 static char *
          532 ftn(char *s)
          533 {
          534         if (strlen(s) == 1)
          535                 return code(sflag ? "<%d>" : "%c", funid(s));
          536         return code("\"()%s\"", estrdup(s));
          537 }
          538 
          539 static char *
          540 var(char *s)
          541 {
          542         if (strlen(s) == 1)
          543                 return code(s);
          544         return code("\"%s\"", estrdup(s));
          545 }
          546 
          547 static void
          548 quit(void)
          549 {
          550         exit(nerr > 0 ? 1 : 0);
          551 }
          552 
          553 static void
          554 skipspaces(void)
          555 {
          556         int ch;
          557 
          558         while (isascii(ch = getc(filep)) && isspace(ch)) {
          559                 if (ch == '\n') {
          560                         lineno++;
          561                         break;
          562                 }
          563         }
          564         ungetc(ch, filep);
          565 }
          566 
          567 static int
          568 iden(int ch)
          569 {
          570         static struct keyword {
          571                 char *str;
          572                 int token;
          573         } keywords[] = {
          574                 {"define", DEF},
          575                 {"break", BREAK},
          576                 {"quit", QUIT},
          577                 {"length", LENGTH},
          578                 {"return", RETURN},
          579                 {"for", FOR},
          580                 {"if", IF},
          581                 {"while", WHILE},
          582                 {"sqrt", SQRT},
          583                 {"scale", SCALE},
          584                 {"ibase", IBASE},
          585                 {"obase", OBASE},
          586                 {"auto", AUTO},
          587                 {"print", PRINT},
          588                 {NULL}
          589         };
          590         struct keyword *p;
          591         char *bp;
          592 
          593         ungetc(ch, filep);
          594         for (bp = yytext; bp < &yytext[BUFSIZ]; ++bp) {
          595                 ch = getc(filep);
          596                 if (!isascii(ch) || !islower(ch))
          597                         break;
          598                 *bp = ch;
          599         }
          600 
          601         if (bp == &yytext[BUFSIZ])
          602                 yyerror("too long token");
          603         *bp = '\0';
          604         ungetc(ch, filep);
          605 
          606         if (strlen(yytext) == 1) {
          607                 strcpy(yylval.id, yytext);
          608                 return ID;
          609         }
          610 
          611         for (p = keywords; p->str && strcmp(p->str, yytext); ++p)
          612                 ;
          613         if (p->str)
          614                 return p->token;
          615 
          616         if (!sflag)
          617                 yyerror("invalid keyword");
          618         strcpy(yylval.id, yytext);
          619         return ID;
          620 }
          621 
          622 static char *
          623 digits(char *bp)
          624 {
          625         int ch;
          626         char *digits = DIGITS, *p;
          627 
          628         while (bp < &yytext[BUFSIZ]) {
          629                 ch = getc(filep);
          630                 p = strchr(digits, ch);
          631                 if (!p)
          632                         break;
          633                 *bp++ = ch;
          634         }
          635 
          636         if (bp == &yytext[BUFSIZ])
          637                 return NULL;
          638         ungetc(ch, filep);
          639 
          640         return bp;
          641 }
          642 
          643 static int
          644 number(int ch)
          645 {
          646         int d;
          647         char *bp;
          648 
          649         ungetc(ch, filep);
          650         if ((bp = digits(yytext)) == NULL)
          651                 goto toolong;
          652 
          653         if ((ch = getc(filep)) != '.') {
          654                 ungetc(ch, filep);
          655                 goto end;
          656         }
          657         *bp++ = '.';
          658 
          659         if ((bp = digits(bp)) == NULL)
          660                 goto toolong;
          661 
          662 end:
          663         if (bp ==  &yytext[BUFSIZ])
          664                 goto toolong;
          665         *bp = '\0';
          666         yylval.str = yytext;
          667 
          668         return NUMBER;
          669 
          670 toolong:
          671         yyerror("too long number");
          672         return 0;
          673 }
          674 
          675 static int
          676 string(int ch)
          677 {
          678         char *bp;
          679 
          680         for (bp = yytext; bp < &yytext[BUFSIZ]; ++bp) {
          681                 if ((ch = getc(filep)) == '"')
          682                         break;
          683                 *bp = ch;
          684         }
          685 
          686         if (bp == &yytext[BUFSIZ])
          687                 yyerror("too long string");
          688         *bp = '\0';
          689         yylval.str = estrdup(yytext);
          690 
          691         return STRING;
          692 }
          693 
          694 static int
          695 follow(int next, int yes, int no)
          696 {
          697         int ch;
          698 
          699         ch = getc(filep);
          700         if (ch == next)
          701                 return yes;
          702         ungetc(ch, filep);
          703         return no;
          704 }
          705 
          706 static int
          707 operand(int ch)
          708 {
          709         int peekc;
          710 
          711         switch (ch) {
          712         case '\n':
          713         case '{':
          714         case '}':
          715         case '[':
          716         case ']':
          717         case '(':
          718         case ')':
          719         case ',':
          720         case ';':
          721                 return ch;
          722         case '.':
          723                 peekc = ungetc(getc(filep), filep);
          724                 if (strchr(DIGITS, peekc))
          725                         return number(ch);
          726                 return DOT;
          727         case '"':
          728                 return string(ch);
          729         case '*':
          730                 yylval.str = "*";
          731                 return follow('=', EQOP, '*');
          732         case '/':
          733                 yylval.str = "/";
          734                 return follow('=', EQOP, '/');
          735         case '%':
          736                 yylval.str = "%";
          737                 return follow('=', EQOP, '%');
          738         case '=':
          739                 return follow('=', EQ, '=');
          740         case '+':
          741         case '-':
          742                 yylval.str = (ch == '+') ? "+" : "-";
          743                 if (follow('=', EQOP, ch) != ch)
          744                         return EQOP;
          745                 return follow(ch, INCDEC, ch);
          746         case '^':
          747                 yylval.str = "^";
          748                 return follow('=', EQOP, '^');
          749         case '<':
          750                 return follow('=', LE, '<');
          751         case '>':
          752                 return follow('=', GE, '>');
          753         case '!':
          754                 if (getc(filep) == '=')
          755                         return NE;
          756         default:
          757                 yyerror("invalid operand");
          758                 return 0;
          759         }
          760 }
          761 
          762 static void
          763 comment(void)
          764 {
          765         int c;
          766 
          767         for (;;) {
          768                 while ((c = getc(filep)) != '*') {
          769                         if (c == '\n')
          770                                 lineno++;
          771                 }
          772                 if ((c = getc(filep)) == '/')
          773                         break;
          774                 ungetc(c, filep);
          775         }
          776 }
          777 
          778 static int
          779 yylex(void)
          780 {
          781         int peekc, ch;
          782 
          783 repeat:
          784         skipspaces();
          785 
          786         ch = getc(filep);
          787         if (ch == EOF) {
          788                 return EOF;
          789         } else if (!isascii(ch)) {
          790                 yyerror("invalid input character");
          791         } else if (islower(ch)) {
          792                 return iden(ch);
          793         } else if (strchr(DIGITS, ch)) {
          794                 return number(ch);
          795         } else {
          796                 if (ch == '/') {
          797                         peekc = getc(filep);
          798                         if (peekc == '*') {
          799                                 comment();
          800                                 goto repeat;
          801                         }
          802                         ungetc(peekc, filep);
          803                 }
          804                 return operand(ch);
          805         }
          806 
          807         return 0;
          808 }
          809 
          810 static void
          811 spawn(void)
          812 {
          813         int fds[2];
          814         char *par = sflag ? "-i" : NULL;
          815         char errmsg[] = "bc:error execing dc\n";
          816 
          817         if (pipe(fds) < 0)
          818                 eprintf("creating pipe:");
          819 
          820         switch (fork()) {
          821         case -1:
          822                 eprintf("forking dc:");
          823         case 0:
          824                 close(1);
          825                 dup(fds[1]);
          826                 close(fds[0]);
          827                 close(fds[1]);
          828                 break;
          829         default:
          830                 close(0);
          831                 dup(fds[0]);
          832                 close(fds[0]);
          833                 close(fds[1]);
          834                 execlp(dcprog, "dc", par, (char *) NULL);
          835 
          836                 /* it shouldn't happen */
          837                 write(3, errmsg, sizeof(errmsg)-1);
          838                 _Exit(2);
          839         }
          840 }
          841 
          842 static void
          843 run(void)
          844 {
          845         if (setjmp(recover)) {
          846                 if (ferror(filep))
          847                         eprintf("%s:", filename);
          848                 if (feof(filep))
          849                         return;
          850         }
          851         yyparse();
          852 }
          853 
          854 static void
          855 bc(char *fname)
          856 {
          857         Macro *d;
          858 
          859         lineno = 1;
          860         nested = 0;
          861 
          862         macro(HOME);
          863         if (!fname) {
          864                 filename = "<stdin>";
          865                 filep = stdin;
          866         } else {
          867                 filename = fname;
          868                 if ((filep = fopen(fname, "r")) == NULL)
          869                         eprintf("%s:", fname);
          870         }
          871 
          872         run();
          873         fclose(filep);
          874 }
          875 
          876 static void
          877 usage(void)
          878 {
          879         eprintf("usage: %s [-p dc][-cdls]\n", argv0);
          880 }
          881 
          882 int
          883 main(int argc, char *argv[])
          884 {
          885         ARGBEGIN {
          886         case 'p':
          887                 dcprog = EARGF(usage());
          888                 break;
          889         case 'c':
          890                 cflag = 1;
          891                 break;
          892         case 'd':
          893                 dflag = 1;
          894                 yydebug = 3;
          895                 break;
          896         case 'l':
          897                 lflag = 1;
          898                 break;
          899         case 's':
          900                 sflag = 1;
          901                 break;
          902         default:
          903                 usage();
          904         } ARGEND
          905 
          906         yytext = malloc(BUFSIZ);
          907         buff = malloc(BUFSIZ);
          908         if (!yytext || !buff)
          909                 eprintf("out of memory\n");
          910         flowid = 128;
          911 
          912         if (!cflag)
          913                 spawn();
          914         if (lflag)
          915                 bc(PREFIX "/share/misc/bc.library");
          916 
          917         while (*argv)
          918                 bc(*argv++);
          919         bc(NULL);
          920 
          921         quit();
          922 }