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
22int yydebug;
23
24typedef struct macro Macro;
25
26struct macro {
27 int op;
28 int id;
29 char *name;
30 int flowid;
31 int nested;
32};
33
34static int yyerror(char *);
35static int yylex(void);
36
37static void quit(void);
38static char *code(char *, ...);
39static char *forcode(Macro *, char *, char *, char *, char *);
40static char *whilecode(Macro *, char *, char *);
41static char *ifcode(Macro *, char *, char *, char *);
42static char *funcode(Macro *, char *, char *, char *);
43static char *param(char *, char *), *local(char *, char *);
44static char *retcode(char *);
45static char *brkcode(void);
46static Macro *macro(int);
47
48static char *ftn(char *);
49static char *var(char *);
50static char *ary(char *);
51static void writeout(char *);
52
53static char *yytext, *buff, *unwind;
54static size_t buffcap;
55static char *filename;
56static FILE *filep;
57static int lineno, nerr, flowid;
58static jmp_buf recover;
59static int nested, inhome;
60static Macro macros[NESTED_MAX];
61int cflag, dflag, lflag, sflag;
62
63static char *dcprog = "dc";
64
65%}
66
67%union {
68 char *str;
69 char id[16]; // matches dc's own REGSIZ: an extended register name's cap
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%token ELSE
97%token HALT
98%token READ
99
100%type <str> item statlst scolonlst
101%type <str> function assign nexpr expr exprstat rel stat ary cond
102%type <str> autolst arglst parlst
103%type <str> prlst pritem
104%type <str> params param locals local
105%type <macro> def if for while
106
107%right '=' EQOP
108%left '+' '-'
109%left '*' '/' '%'
110%right '^'
111
112%start program
113
114%%
115
116program :
117 | item program
118 ;
119
120item : scolonlst '\n' {writeout($1);}
121 | function {writeout($1);}
122 ;
123
124function : def parlst '{' '\n' autolst statlst '}' {$$ = funcode($1, $2, $5, $6);}
125 ;
126
127scolonlst: {$$ = code("");}
128 | stat
129 | scolonlst ';' stat {$$ = code("%s%s", $1, $3);}
130 | scolonlst ';'
131 ;
132
133statlst : {$$ = code("");}
134 | stat
135 | statlst '\n' stat {$$ = code("%s%s", $1, $3);}
136 | statlst ';' stat {$$ = code("%s%s", $1, $3);}
137 | statlst '\n'
138 | statlst ';'
139 ;
140
141stat : exprstat
142 | PRINT prlst {$$ = $2;}
143 | STRING {$$ = code("[%s]P", $1);}
144 | BREAK {$$ = brkcode();}
145 | QUIT {quit();}
146 | HALT {$$ = code("q");}
147 | RETURN {$$ = retcode(code(" 0"));}
148 | RETURN expr {$$ = retcode($2);}
149 | RETURN '(' ')' {$$ = retcode(code(" 0"));}
150 | while cond optnl stat {$$ = whilecode($1, $2, $4);}
151 | if cond optnl stat {$$ = ifcode($1, $2, $4, NULL);}
152 | if cond optnl stat ELSE optnl stat {$$ = ifcode($1, $2, $4, $7);}
153 | '{' statlst '}' {$$ = $2;}
154 | for '(' expr ';' rel ';' expr ')' optnl stat {$$ = forcode($1, $3, $5, $7, $10);}
155 ;
156
157// real scripts routinely put a while/if/for's own body on the next
158// line instead of right after the condition; nothing else in this
159// grammar needs a newline here, since a body wrapped in { } already
160// goes through statlst, which handles its own internal newlines
161optnl :
162 | optnl '\n'
163 ;
164
165// print takes a comma-separated list of any length, mixing strings
166// and expressions freely: real scripts (the kernel's own timeconst.bc
167// generator among them) routinely print three or more at once. no
168// item here ever gets an automatic trailing newline of its own,
169// matching real bc: whatever newlines appear come from the items
170// themselves
171prlst : pritem
172 | prlst ',' pritem {$$ = code("%s%s", $1, $3);}
173 ;
174
175pritem : expr {$$ = code("%sn", $1);}
176 | STRING {$$ = code("[%s]P", $1);}
177 ;
178
179while : WHILE {$$ = macro(LOOP);}
180 ;
181
182if : IF {$$ = macro(IF);}
183 ;
184
185for : FOR {$$ = macro(LOOP);}
186 ;
187
188def : DEF ID {$$ = macro(DEF);}
189 ;
190
191parlst : '(' ')' {$$ = code("");}
192 | '(' params ')' {$$ = $2;}
193 ;
194
195params : param {$$ = param(NULL, $1);}
196 | params ',' param {$$ = param($1, $3);}
197 ;
198
199param : ID {$$ = var($1);}
200 | ID '[' ']' {$$ = ary($1);}
201 ;
202
203autolst : {$$ = code("");}
204 | AUTO locals '\n' {$$ = $2;}
205 | AUTO locals ';' {$$ = $2;}
206 ;
207
208locals : local {$$ = local(NULL, $1);}
209 | locals ',' local {$$ = local($1, $3);}
210 ;
211
212local : ID {$$ = var($1);}
213 | ID '[' ']' {$$ = ary($1);}
214 ;
215
216arglst : expr
217 | ID '[' ']' {$$ = code("%s", ary($1));}
218 | expr ',' arglst {$$ = code("%s%s", $1, $3);}
219 | ID '[' ']' ',' arglst {$$ = code("%s%s", ary($1), $5);}
220 ;
221
222cond : '(' rel ')' {$$ = $2;}
223 ;
224
225rel : expr {$$ = code("%s 0!=", $1);}
226 | expr EQ expr {$$ = code("%s%s=", $1, $3);}
227 | expr LE expr {$$ = code("%s%s!<", $1, $3);}
228 | expr GE expr {$$ = code("%s%s!>", $1, $3);}
229 | expr NE expr {$$ = code("%s%s!=", $1, $3);}
230 | expr '<' expr {$$ = code("%s%s>", $1, $3);}
231 | expr '>' expr {$$ = code("%s%s<", $1, $3);}
232 ;
233
234exprstat: nexpr {$$ = code("%s%ss.", $1, code(sflag ? "" : "p"));}
235 | assign {$$ = code("%ss.", $1);}
236 ;
237
238expr : nexpr
239 | assign
240 ;
241
242nexpr : NUMBER {$$ = code(" %s", code($1));}
243 | ID {$$ = code("l%s", var($1));}
244 | READ '(' ')' {$$ = code("?");}
245 | DOT {$$ = code("l.");}
246 | SCALE {$$ = code("K");}
247 | IBASE {$$ = code("I");}
248 | OBASE {$$ = code("O");}
249 | ID ary {$$ = code("%s;%s", $2, ary($1));}
250 | '(' expr ')' {$$ = $2;}
251 | ID '(' arglst ')' {$$ = code("%sl%sx", $3, ftn($1));}
252 | ID '(' ')' {$$ = code("l%sx", ftn($1));}
253 | '-' expr {$$ = code("0%s-", $2);}
254 | expr '+' expr {$$ = code("%s%s+", $1, $3);}
255 | expr '-' expr {$$ = code("%s%s-", $1, $3);}
256 | expr '*' expr {$$ = code("%s%s*", $1, $3);}
257 | expr '/' expr {$$ = code("%s%s/", $1, $3);}
258 | expr '%' expr {$$ = code("%s%s%%", $1, $3);}
259 | expr '^' expr {$$ = code("%s%s^", $1, $3);}
260 | LENGTH '(' expr ')' {$$ = code("%sZ", $3);}
261 | SQRT '(' expr ')' {$$ = code("%sv", $3);}
262 | SCALE '(' expr ')' {$$ = code("%sX", $3);}
263 | INCDEC ID {$$ = code("l%s1%sds%s", var($2), code($1), var($2));}
264 | INCDEC SCALE {$$ = code("K1%sk", code($1));}
265 | INCDEC IBASE {$$ = code("I1%sdi", code($1));}
266 | INCDEC OBASE {$$ = code("O1%sdo", code($1));}
267 | INCDEC ID ary {$$ = code("%sdS_;%s1%sdL_:%s", $3, ary($2), code($1), ary($2));}
268 | ID INCDEC {$$ = code("l%sd1%ss%s", var($1), code($2), var($1));}
269 | SCALE INCDEC {$$ = code("Kd1%sk", code($2));}
270 | IBASE INCDEC {$$ = code("Id1%si", code($2));}
271 | OBASE INCDEC {$$ = code("Od1%so", code($2));}
272 | ID ary INCDEC {$$ = code("%sds.;%sd1%sl.:%s", $2, ary($1), code($3), ary($1));}
273 ;
274
275assign : ID '=' expr {$$ = code("%sds%s", $3, var($1));}
276 | SCALE '=' expr {$$ = code("%sdk", $3);}
277 | IBASE '=' expr {$$ = code("%sdi", $3);}
278 | OBASE '=' expr {$$ = code("%sdo", $3);}
279 | ID ary '=' expr {$$ = code("%sd%s:%s", $4, $2, ary($1));}
280 | ID EQOP expr {$$ = code("%sl%s%sds%s", $3, var($1), code($2), var($1));}
281 | SCALE EQOP expr {$$ = code("%sK%sdk", $3, code($2));}
282 | IBASE EQOP expr {$$ = code("%sI%sdi", $3, code($2));}
283 | OBASE EQOP expr {$$ = code("%sO%sdo", $3, code($2));}
284 | ID ary EQOP expr {$$ = code("%s%sds.;%s%sdl.:s", $4, $2, ary($1), code($3), ary($1));}
285 ;
286
287ary : '[' expr ']' {$$ = $2;}
288 ;
289
290%%
291static int
292yyerror(char *s)
293{
294 fprintf(stderr, "bc: %s:%d: %s\n", filename, lineno, s);
295 nerr++;
296 longjmp(recover, 1);
297}
298
299static void
300writeout(char *s)
301{
302 if (write(1, s, strlen(s)) < 0)
303 goto err;
304 if (write(1, "\n", 1) < 0)
305 goto err;
306 free(s);
307 return;
308
309err:
310 eprintf("writing to dc:");
311}
312
313// grows the shared code buffer geometrically to fit at least 'need'
314// bytes: a single generated dc macro (an if/else body, a print list,
315// a deeply nested function) has no fixed upper bound, unlike the
316// stdio-buffering constant BUFSIZ this buffer borrowed its original
317// size from
318static void
319growbuff(size_t need)
320{
321 size_t newcap;
322
323 newcap = buffcap;
324 while (newcap < need)
325 newcap *= 2;
326 buff = erealloc(buff, newcap);
327 buffcap = newcap;
328}
329
330static char *
331code(char *fmt, ...)
332{
333 char *t;
334 va_list ap;
335 int c, len;
336 size_t off, room;
337
338 va_start(ap, fmt);
339 for (off = 0; *fmt; off += (size_t)len) {
340 len = 1;
341 if ((c = *fmt++) != '%')
342 goto append;
343
344 switch (*fmt++) {
345 case 'd':
346 c = va_arg(ap, int);
347 for (;;) {
348 room = buffcap - off;
349 len = snprintf(buff + off, room, "%d", c);
350 if (len < 0)
351 eprintf("unable to code requested operation\n");
352 if ((size_t)len < room)
353 break;
354 growbuff(off + (size_t)len + 1);
355 }
356 break;
357 case 'c':
358 c = va_arg(ap, int);
359 goto append;
360 case 's':
361 t = va_arg(ap, void *);
362 len = strlen(t);
363 if ((size_t)len >= buffcap - off)
364 growbuff(off + (size_t)len + 1);
365 memcpy(buff + off, t, len);
366 free(t);
367 break;
368 case '%':
369 append:
370 if (buffcap - off <= 1)
371 growbuff(off + 2);
372 buff[off] = c;
373 break;
374 default:
375 abort();
376 }
377 }
378 va_end(ap);
379
380 buff[off] = '\0';
381 return estrdup(buff);
382}
383
384static Macro *
385macro(int op)
386{
387 int preop;
388 Macro *d, *p;
389
390 if (nested == NESTED_MAX)
391 yyerror("too much nesting");
392
393 d = ¯os[nested];
394 d->op = op;
395 d->nested = nested++;
396 d->name = NULL;
397
398 switch (op) {
399 case HOME:
400 d->id = 0;
401 d->flowid = flowid;
402 inhome = 1;
403 break;
404 case DEF:
405 unwind = estrdup("");
406 inhome = 0;
407 d->id = funid(yytext);
408 d->name = estrdup(yytext);
409 d->flowid = macros[0].flowid;
410 break;
411 default:
412 assert(nested > 1);
413 preop = d[-1].op;
414 d->flowid = d[-1].flowid;
415 if (preop != HOME && preop != DEF) {
416 if (d->flowid == 255)
417 eprintf("too many control flow structures");
418 d->flowid++;
419 }
420 d->id = d->flowid;
421 if (!inhome) {
422 /* populate reserved id */
423 flowid = d->flowid;
424 for (p = d; p != macros; --p)
425 p[-1].flowid++;
426 }
427 break;
428 }
429
430 return d;
431}
432
433static char *
434decl(int type, char *list, char *id)
435{
436 char *i1, *i2;
437
438 i1 = estrdup(id);
439 i2 = estrdup(id);
440 free(id);
441
442 if (!list)
443 list = estrdup("");
444
445 unwind = code("%sL%ss.", unwind, i1);
446
447 return code((type == AUTO) ? "0S%s%s" : "S%s%s", i2, list);
448}
449
450static char *
451param(char *list, char *id)
452{
453 return decl(PARAM, list, id);
454}
455
456static char *
457local(char *list, char *id)
458{
459 return decl(AUTO, list, id);
460}
461
462static char *
463funcode(Macro *d, char *params, char *vars, char *body)
464{
465 char *s;
466
467 if (strlen(d->name) > 1) {
468 s = code("[%s%s%s%s]s\"()%s\"",
469 vars, params,
470 body,
471 retcode(code(" 0")),
472 d->name);
473 } else {
474 s = code(sflag ? "[%s%s%s%s]s<%d>" : "[%s%s%s%s]s%c",
475 vars, params,
476 body,
477 retcode(code(" 0")),
478 d->id);
479 free(d->name);
480 }
481
482 free(unwind);
483 unwind = NULL;
484 nested--;
485 inhome = 0;
486
487 return s;
488}
489
490static char *
491brkcode(void)
492{
493 Macro *d;
494
495 for (d = ¯os[nested-1]; d->op != HOME && d->op != LOOP; --d)
496 ;
497 if (d->op == HOME)
498 yyerror("break not in for or while");
499 return code(" %dQ", nested - d->nested);
500}
501
502static char *
503forcode(Macro *d, char *init, char *cmp, char *inc, char *body)
504{
505 char *s;
506
507 s = code(sflag ? "[%s%ss.%s<%d>]s<%d>" : "[%s%ss.%s%c]s%c",
508 body,
509 inc,
510 estrdup(cmp),
511 d->id, d->id);
512 writeout(s);
513
514 s = code(sflag ? "%ss.%s<%d> " : "%ss.%s%c ",
515 init,
516 cmp,
517 d->id);
518 nested--;
519
520 return s;
521}
522
523static char *
524whilecode(Macro *d, char *cmp, char *body)
525{
526 char *s;
527
528 s = code(sflag ? "[%s%s<%d>]s<%d>" : "[%s%s%c]s%c",
529 body,
530 estrdup(cmp),
531 d->id, d->id);
532 writeout(s);
533
534 s = code(sflag ? "%s<%d> " : "%s%c ",
535 cmp, d->id);
536 nested--;
537
538 return s;
539}
540
541// the trailing relational operator on a cond's own generated code
542// (see the rel grammar rule) is always one of these six forms; an
543// else branch runs on whichever of them means "the opposite happened"
544static char *
545invertrel(char *cmp)
546{
547 size_t n = strlen(cmp);
548 const char *suffix;
549 size_t keep;
550 char *s;
551
552 if (n >= 2 && strcmp(cmp + n - 2, "!=") == 0) {
553 keep = n - 2;
554 suffix = "=";
555 } else if (n >= 2 && strcmp(cmp + n - 2, "!<") == 0) {
556 keep = n - 2;
557 suffix = "<";
558 } else if (n >= 2 && strcmp(cmp + n - 2, "!>") == 0) {
559 keep = n - 2;
560 suffix = ">";
561 } else if (n >= 1 && cmp[n - 1] == '=') {
562 keep = n - 1;
563 suffix = "!=";
564 } else if (n >= 1 && cmp[n - 1] == '<') {
565 keep = n - 1;
566 suffix = "!<";
567 } else if (n >= 1 && cmp[n - 1] == '>') {
568 keep = n - 1;
569 suffix = "!>";
570 } else {
571 yyerror("cannot invert comparison for else");
572 return NULL;
573 }
574
575 s = emalloc(keep + strlen(suffix) + 1);
576 memcpy(s, cmp, keep);
577 strcpy(s + keep, suffix);
578
579 return s;
580}
581
582// an else branch's own stored body needs a register distinct from the
583// then branch's d->id: nested caps how high d->id itself ever goes,
584// so offsetting by it lands on a register no other nesting level, and
585// no ordinary (single-letter or, under -i, named) variable, ever uses
586static char *
587ifcode(Macro *d, char *cmp, char *body, char *ebody)
588{
589 char *s, *inv;
590
591 s = code(sflag ? "[%s]s<%d>" : "[%s]s%c",
592 body, d->id);
593 writeout(s);
594
595 if (ebody) {
596 s = code(sflag ? "[%s]s<%d>" : "[%s]s%c",
597 ebody, d->id + NESTED_MAX);
598 writeout(s);
599
600 inv = invertrel(cmp);
601 s = code(sflag ? "%s<%d> %s<%d> " : "%s%c %s%c ",
602 cmp, d->id, inv, d->id + NESTED_MAX);
603 } else {
604 s = code(sflag ? "%s<%d> " : "%s%c ",
605 cmp, d->id);
606 }
607 nested--;
608
609 return s;
610}
611
612static char *
613retcode(char *expr)
614{
615 if (nested < 2 || macros[1].op != DEF)
616 yyerror("return must be in a function");
617 return code("%s %s %dQ", expr, estrdup(unwind), nested - 1);
618}
619
620static char *
621ary(char *s)
622{
623 if (strlen(s) == 1)
624 return code("%c", toupper(s[0]));
625 return code("\"[]%s\"", estrdup(s));
626}
627
628static char *
629ftn(char *s)
630{
631 if (strlen(s) == 1)
632 return code(sflag ? "<%d>" : "%c", funid(s));
633 return code("\"()%s\"", estrdup(s));
634}
635
636static char *
637var(char *s)
638{
639 if (strlen(s) == 1)
640 return code(s);
641 return code("\"%s\"", estrdup(s));
642}
643
644static void
645quit(void)
646{
647 exit(nerr > 0 ? 1 : 0);
648}
649
650static void
651skipspaces(void)
652{
653 int ch;
654
655 while (isascii(ch = getc(filep)) && isspace(ch)) {
656 if (ch == '\n') {
657 lineno++;
658 break;
659 }
660 }
661 ungetc(ch, filep);
662}
663
664static int
665iden(int ch)
666{
667 static struct keyword {
668 char *str;
669 int token;
670 } keywords[] = {
671 {"define", DEF},
672 {"break", BREAK},
673 {"quit", QUIT},
674 {"length", LENGTH},
675 {"return", RETURN},
676 {"for", FOR},
677 {"if", IF},
678 {"while", WHILE},
679 {"sqrt", SQRT},
680 {"scale", SCALE},
681 {"ibase", IBASE},
682 {"obase", OBASE},
683 {"auto", AUTO},
684 {"print", PRINT},
685 {"else", ELSE},
686 {"halt", HALT},
687 {"read", READ},
688 {NULL}
689 };
690 struct keyword *p;
691 char *bp;
692
693 ungetc(ch, filep);
694 for (bp = yytext; bp < &yytext[BUFSIZ]; ++bp) {
695 ch = getc(filep);
696 if (!isascii(ch) || !islower(ch))
697 break;
698 *bp = ch;
699 }
700
701 if (bp == &yytext[BUFSIZ])
702 yyerror("too long token");
703 *bp = '\0';
704 ungetc(ch, filep);
705
706 if (strlen(yytext) == 1) {
707 strcpy(yylval.id, yytext);
708 return ID;
709 }
710
711 for (p = keywords; p->str && strcmp(p->str, yytext); ++p)
712 ;
713 if (p->str)
714 return p->token;
715
716 // strict POSIX bc only ever names a variable/function with a
717 // single lowercase letter; every real bc script in the wild
718 // (the kernel's own timeconst.bc among them) freely uses
719 // multi-character names anyway, same as GNU bc's own default
720#if !FEATURE_BC_MULTICHAR
721 if (!sflag)
722 yyerror("invalid keyword");
723#endif
724 if (strlen(yytext) >= sizeof(yylval.id))
725 yyerror("identifier too long");
726 strcpy(yylval.id, yytext);
727 return ID;
728}
729
730static char *
731digits(char *bp)
732{
733 int ch;
734 char *digits = DIGITS, *p;
735
736 while (bp < &yytext[BUFSIZ]) {
737 ch = getc(filep);
738 p = strchr(digits, ch);
739 if (!p)
740 break;
741 *bp++ = ch;
742 }
743
744 if (bp == &yytext[BUFSIZ])
745 return NULL;
746 ungetc(ch, filep);
747
748 return bp;
749}
750
751static int
752number(int ch)
753{
754 char *bp;
755
756 ungetc(ch, filep);
757 if ((bp = digits(yytext)) == NULL)
758 goto toolong;
759
760 if ((ch = getc(filep)) != '.') {
761 ungetc(ch, filep);
762 goto end;
763 }
764 *bp++ = '.';
765
766 if ((bp = digits(bp)) == NULL)
767 goto toolong;
768
769end:
770 if (bp == &yytext[BUFSIZ])
771 goto toolong;
772 *bp = '\0';
773 yylval.str = yytext;
774
775 return NUMBER;
776
777toolong:
778 yyerror("too long number");
779 return 0;
780}
781
782static int
783string(int ch)
784{
785 char *bp;
786
787 for (bp = yytext; bp < &yytext[BUFSIZ]; ++bp) {
788 if ((ch = getc(filep)) == '"')
789 break;
790#if FEATURE_BC_ESCAPES
791 // strict POSIX bc has no escape sequences in a string at
792 // all; every real script (again, the kernel's own scripts
793 // among them) assumes GNU bc's \n/\t/etc, since dc's own
794 // string reader passes a literal byte straight through
795 // with nothing left to turn it into a real newline later
796 if (ch == '\\') {
797 switch (ch = getc(filep)) {
798 case 'a':
799 ch = '\a';
800 break;
801 case 'b':
802 ch = '\b';
803 break;
804 case 'f':
805 ch = '\f';
806 break;
807 case 'n':
808 ch = '\n';
809 break;
810 case 'r':
811 ch = '\r';
812 break;
813 case 't':
814 ch = '\t';
815 break;
816 case 'v':
817 ch = '\v';
818 break;
819 case 'q':
820 // GNU bc's own escape for a literal double
821 // quote, needed since a bc string's own
822 // delimiter is " with no other way in
823 ch = '"';
824 break;
825 case '\\':
826 case '"':
827 break;
828 default:
829 *bp++ = '\\';
830 break;
831 }
832 }
833#endif
834 *bp = ch;
835 }
836
837 if (bp == &yytext[BUFSIZ])
838 yyerror("too long string");
839 *bp = '\0';
840 yylval.str = estrdup(yytext);
841
842 return STRING;
843}
844
845static int
846follow(int next, int yes, int no)
847{
848 int ch;
849
850 ch = getc(filep);
851 if (ch == next)
852 return yes;
853 ungetc(ch, filep);
854 return no;
855}
856
857static int
858operand(int ch)
859{
860 int peekc;
861
862 switch (ch) {
863 case '\n':
864 case '{':
865 case '}':
866 case '[':
867 case ']':
868 case '(':
869 case ')':
870 case ',':
871 case ';':
872 return ch;
873 case '.':
874 peekc = ungetc(getc(filep), filep);
875 if (strchr(DIGITS, peekc))
876 return number(ch);
877 return DOT;
878 case '"':
879 return string(ch);
880 case '*':
881 yylval.str = "*";
882 return follow('=', EQOP, '*');
883 case '/':
884 yylval.str = "/";
885 return follow('=', EQOP, '/');
886 case '%':
887 yylval.str = "%";
888 return follow('=', EQOP, '%');
889 case '=':
890 return follow('=', EQ, '=');
891 case '+':
892 case '-':
893 yylval.str = (ch == '+') ? "+" : "-";
894 if (follow('=', EQOP, ch) != ch)
895 return EQOP;
896 return follow(ch, INCDEC, ch);
897 case '^':
898 yylval.str = "^";
899 return follow('=', EQOP, '^');
900 case '<':
901 return follow('=', LE, '<');
902 case '>':
903 return follow('=', GE, '>');
904 case '!':
905 if (getc(filep) == '=')
906 return NE;
907 /* fallthrough */
908 default:
909 yyerror("invalid operand");
910 return 0;
911 }
912}
913
914static void
915comment(void)
916{
917 int c;
918
919 for (;;) {
920 while ((c = getc(filep)) != '*') {
921 if (c == '\n')
922 lineno++;
923 }
924 if ((c = getc(filep)) == '/')
925 break;
926 ungetc(c, filep);
927 }
928}
929
930static int
931yylex(void)
932{
933 int peekc, ch;
934
935repeat:
936 skipspaces();
937
938 ch = getc(filep);
939 if (ch == EOF) {
940 return EOF;
941 } else if (!isascii(ch)) {
942 yyerror("invalid input character");
943 } else if (islower(ch)) {
944 return iden(ch);
945 } else if (strchr(DIGITS, ch)) {
946 return number(ch);
947 } else {
948 if (ch == '/') {
949 peekc = getc(filep);
950 if (peekc == '*') {
951 comment();
952 goto repeat;
953 }
954 ungetc(peekc, filep);
955 }
956 return operand(ch);
957 }
958
959 return 0;
960}
961
962static void
963spawn(void)
964{
965 int fds[2];
966 // dc only accepts the extended, multi-character register
967 // syntax bc.y's own ftn()/var()/ary() emit for a non-single-
968 // letter name (see iden() above) when told -i: whenever this
969 // build can produce that syntax at all, dc always needs it
970#if FEATURE_BC_MULTICHAR
971 char *par = "-i";
972#else
973 char *par = sflag ? "-i" : NULL;
974#endif
975 char errmsg[] = "bc:error execing dc\n";
976
977 // fd 0 is about to become the read end of the code pipe below,
978 // but dc's own "?" (bc's read()) needs the real, original stdin:
979 // stash it on a fixed fd dc knows to check, or read() would read
980 // straight out of this project's own generated dc code instead
981 // of whatever the user actually piped in. this has to happen
982 // before pipe() claims its own two fds, or dup2 here could
983 // clobber one of them (fd 3 is often the next one pipe() itself
984 // would have picked)
985 dup2(0, 3);
986
987 if (pipe(fds) < 0)
988 eprintf("creating pipe:");
989
990 switch (fork()) {
991 case -1:
992 eprintf("forking dc:");
993 /* fallthrough */
994 case 0:
995 close(1);
996 dup(fds[1]);
997 close(fds[0]);
998 close(fds[1]);
999 break;
1000 default:
1001 close(0);
1002 dup(fds[0]);
1003 close(fds[0]);
1004 close(fds[1]);
1005 execlp(dcprog, "dc", par, (char *) NULL);
1006
1007 /* it shouldn't happen */
1008 write(3, errmsg, sizeof(errmsg)-1);
1009 _Exit(2);
1010 }
1011}
1012
1013static void
1014run(void)
1015{
1016 if (setjmp(recover)) {
1017 if (ferror(filep))
1018 eprintf("%s:", filename);
1019 if (feof(filep))
1020 return;
1021 }
1022 yyparse();
1023}
1024
1025static void
1026bc(char *fname)
1027{
1028 lineno = 1;
1029 nested = 0;
1030
1031 macro(HOME);
1032 if (!fname) {
1033 filename = "<stdin>";
1034 filep = stdin;
1035 } else {
1036 filename = fname;
1037 if ((filep = fopen(fname, "r")) == NULL)
1038 eprintf("%s:", fname);
1039 }
1040
1041 run();
1042 fclose(filep);
1043}
1044
1045static void
1046usage(void)
1047{
1048#if FEATURE_BC_QUIET
1049 eprintf("usage: %s [-p dc][-cdlqs]\n", argv0);
1050#else
1051 eprintf("usage: %s [-p dc][-cdls]\n", argv0);
1052#endif
1053}
1054
1055int
1056main(int argc, char *argv[])
1057{
1058 ARGBEGIN {
1059 case 'p':
1060 dcprog = EARGF(usage());
1061 break;
1062 case 'c':
1063 cflag = 1;
1064 break;
1065 case 'd':
1066 dflag = 1;
1067 yydebug = 3;
1068 break;
1069 case 'l':
1070 lflag = 1;
1071 break;
1072#if FEATURE_BC_QUIET
1073 // for compat with sucky impls
1074 case 'q':
1075 break;
1076#endif
1077 case 's':
1078 sflag = 1;
1079 break;
1080 default:
1081 usage();
1082 } ARGEND
1083
1084 yytext = malloc(BUFSIZ);
1085 buffcap = BUFSIZ;
1086 buff = malloc(buffcap);
1087 if (!yytext || !buff)
1088 eprintf("out of memory\n");
1089 flowid = 128;
1090
1091 if (!cflag)
1092 spawn();
1093 if (lflag)
1094 bc(PREFIX "/share/misc/bc.library");
1095
1096 // stdin is only the script source when no file was given at all:
1097 // a real file argument alongside piped stdin data (the kernel's
1098 // own "echo $HZ | bc -q file.bc" being the standing example) means
1099 // that data is meant for read(), not for bc itself to parse as
1100 // more of the program
1101 if (!*argv) {
1102 bc(NULL);
1103 } else {
1104 while (*argv)
1105 bc(*argv++);
1106 }
1107
1108 quit();
1109}