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 *);
42static char *funcode(Macro *, char *, char *, char *);
43static char *param(char *, char *), *local(char *, char *);
44static Macro *define(char *, char *);
45static char *retcode(char *);
46static char *brkcode(void);
47static Macro *macro(int);
48
49static char *ftn(char *);
50static char *var(char *);
51static char *ary(char *);
52static void writeout(char *);
53
54static char *yytext, *buff, *unwind;
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[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
112program :
113 | item program
114 ;
115
116item : scolonlst '\n' {writeout($1);}
117 | function {writeout($1);}
118 ;
119
120function : def parlst '{' '\n' autolst statlst '}' {$$ = funcode($1, $2, $5, $6);}
121 ;
122
123scolonlst: {$$ = code("");}
124 | stat
125 | scolonlst ';' stat {$$ = code("%s%s", $1, $3);}
126 | scolonlst ';'
127 ;
128
129statlst : {$$ = 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
137stat : 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
153while : WHILE {$$ = macro(LOOP);}
154 ;
155
156if : IF {$$ = macro(IF);}
157 ;
158
159for : FOR {$$ = macro(LOOP);}
160 ;
161
162def : DEF ID {$$ = macro(DEF);}
163 ;
164
165parlst : '(' ')' {$$ = code("");}
166 | '(' params ')' {$$ = $2;}
167 ;
168
169params : param {$$ = param(NULL, $1);}
170 | params ',' param {$$ = param($1, $3);}
171 ;
172
173param : ID {$$ = var($1);}
174 | ID '[' ']' {$$ = ary($1);}
175 ;
176
177autolst : {$$ = code("");}
178 | AUTO locals '\n' {$$ = $2;}
179 | AUTO locals ';' {$$ = $2;}
180 ;
181
182locals : local {$$ = local(NULL, $1);}
183 | locals ',' local {$$ = local($1, $3);}
184 ;
185
186local : ID {$$ = var($1);}
187 | ID '[' ']' {$$ = ary($1);}
188 ;
189
190arglst : 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
196cond : '(' rel ')' {$$ = $2;}
197 ;
198
199rel : 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
208exprstat: nexpr {$$ = code("%s%ss.", $1, code(sflag ? "" : "p"));}
209 | assign {$$ = code("%ss.", $1);}
210 ;
211
212expr : nexpr
213 | assign
214 ;
215
216nexpr : 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
248assign : 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
260ary : '[' expr ']' {$$ = $2;}
261 ;
262
263%%
264static int
265yyerror(char *s)
266{
267 fprintf(stderr, "bc: %s:%d: %s\n", filename, lineno, s);
268 nerr++;
269 longjmp(recover, 1);
270}
271
272static void
273writeout(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
282err:
283 eprintf("writing to dc:");
284}
285
286static char *
287code(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
335err:
336 eprintf("unable to code requested operation\n");
337 return NULL;
338}
339
340static Macro *
341macro(int op)
342{
343 int preop;
344 Macro *d, *p;
345
346 if (nested == NESTED_MAX)
347 yyerror("too much nesting");
348
349 d = ¯os[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
389static char *
390decl(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
406static char *
407param(char *list, char *id)
408{
409 return decl(PARAM, list, id);
410}
411
412static char *
413local(char *list, char *id)
414{
415 return decl(AUTO, list, id);
416}
417
418static char *
419funcode(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
446static char *
447brkcode(void)
448{
449 Macro *d;
450
451 for (d = ¯os[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
458static char *
459forcode(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
479static char *
480whilecode(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
497static char *
498ifcode(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
513static char *
514retcode(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
523static char *
524ary(char *s)
525{
526 if (strlen(s) == 1)
527 return code("%c", toupper(s[0]));
528 return code("\"[]%s\"", estrdup(s));
529}
530
531static char *
532ftn(char *s)
533{
534 if (strlen(s) == 1)
535 return code(sflag ? "<%d>" : "%c", funid(s));
536 return code("\"()%s\"", estrdup(s));
537}
538
539static char *
540var(char *s)
541{
542 if (strlen(s) == 1)
543 return code(s);
544 return code("\"%s\"", estrdup(s));
545}
546
547static void
548quit(void)
549{
550 exit(nerr > 0 ? 1 : 0);
551}
552
553static void
554skipspaces(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
567static int
568iden(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
622static char *
623digits(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
643static int
644number(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
662end:
663 if (bp == &yytext[BUFSIZ])
664 goto toolong;
665 *bp = '\0';
666 yylval.str = yytext;
667
668 return NUMBER;
669
670toolong:
671 yyerror("too long number");
672 return 0;
673}
674
675static int
676string(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
694static int
695follow(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
706static int
707operand(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
762static void
763comment(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
778static int
779yylex(void)
780{
781 int peekc, ch;
782
783repeat:
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
810static void
811spawn(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
842static void
843run(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
854static void
855bc(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
876static void
877usage(void)
878{
879 eprintf("usage: %s [-p dc][-cdls]\n", argv0);
880}
881
882int
883main(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}