sbase

suckless unix tools
git clone git://git.suckless.org/sbase
Log | Files | Refs | README | LICENSE

bc.y (17798B)


      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, (char[]){'\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 }