xref: /dragonfly/usr.bin/bc/bc.y (revision b40e316c)
1 %{
2 /*
3  * $OpenBSD: bc.y,v 1.23 2004/02/18 07:43:58 otto Exp $
4  * $DragonFly: src/usr.bin/bc/bc.y,v 1.1 2004/09/20 04:20:34 dillon Exp $
5  */
6 
7 /*
8  * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net>
9  *
10  * Permission to use, copy, modify, and distribute this software for any
11  * purpose with or without fee is hereby granted, provided that the above
12  * copyright notice and this permission notice appear in all copies.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  */
22 
23 /*
24  * This implementation of bc(1) uses concepts from the original 4.4
25  * BSD bc(1). The code itself is a complete rewrite, based on the
26  * Posix defined bc(1) grammar. Other differences include type safe
27  * usage of pointers to build the tree of emitted code, typed yacc
28  * rule values, dynamic allocation of all data structures and a
29  * completely rewritten lexical analyzer using lex(1).
30  *
31  * Some effort has been made to make sure that the generated code is
32  * the same as the code generated by the older version, to provide
33  * easy regression testing.
34  */
35 
36 #include <ctype.h>
37 #include <err.h>
38 #include <limits.h>
39 #include <search.h>
40 #include <signal.h>
41 #include <stdarg.h>
42 #include <stdbool.h>
43 #include <string.h>
44 #include <unistd.h>
45 
46 #include "extern.h"
47 #include "pathnames.h"
48 
49 #define END_NODE	((ssize_t) -1)
50 #define CONST_STRING	((ssize_t) -2)
51 #define ALLOC_STRING	((ssize_t) -3)
52 
53 struct tree {
54 	ssize_t			index;
55 	union {
56 		char		*astr;
57 		const char	*cstr;
58 	} u;
59 };
60 
61 int			yyparse(void);
62 int			yywrap(void);
63 
64 static void		grow(void);
65 static ssize_t		cs(const char *);
66 static ssize_t		as(const char *);
67 static ssize_t		node(ssize_t, ...);
68 static void		emit(ssize_t);
69 static void		emit_macro(int, ssize_t);
70 static void		free_tree(void);
71 static ssize_t		numnode(int);
72 static ssize_t		lookup(char *, size_t, char);
73 static ssize_t		letter_node(char *);
74 static ssize_t		array_node(char *);
75 static ssize_t		function_node(char *);
76 
77 static void		add_par(ssize_t);
78 static void		add_local(ssize_t);
79 static void		warning(const char *);
80 static void		init(void);
81 static void		usage(void);
82 static char		*escape(const char *);
83 
84 static size_t		instr_sz = 0;
85 static struct tree	*instructions = NULL;
86 static ssize_t		current = 0;
87 static int		macro_char = '0';
88 static int		reset_macro_char = '0';
89 static int		nesting = 0;
90 static int		breakstack[16];
91 static int		breaksp = 0;
92 static ssize_t		prologue;
93 static ssize_t		epilogue;
94 static bool		st_has_continue;
95 static char		str_table[UCHAR_MAX][2];
96 static int		fileindex;
97 static int		sargc;
98 static char		**sargv;
99 static char		*filename;
100 static bool		do_fork = true;
101 static u_short		var_count;
102 
103 extern char *__progname;
104 
105 #define BREAKSTACK_SZ	(sizeof(breakstack)/sizeof(breakstack[0]))
106 
107 /* These values are 4.4BSD bc compatible */
108 #define FUNC_CHAR	0x01
109 #define ARRAY_CHAR	0xa1
110 
111 /* Skip '\0', [, \ and ] */
112 #define ENCODE(c)	((c) < '[' ? (c) : (c) + 3);
113 #define VAR_BASE	(256-4)
114 #define MAX_VARIABLES	(VAR_BASE * VAR_BASE)
115 
116 %}
117 
118 %start program
119 
120 %union {
121 	ssize_t		node;
122 	struct lvalue	lvalue;
123 	const char	*str;
124 	char		*astr;
125 }
126 
127 %token COMMA SEMICOLON LPAR RPAR LBRACE RBRACE LBRACKET RBRACKET DOT
128 %token NEWLINE
129 %token <astr> LETTER
130 %token <str> NUMBER STRING
131 %token DEFINE BREAK QUIT LENGTH
132 %token RETURN FOR IF WHILE SQRT
133 %token SCALE IBASE OBASE AUTO
134 %token CONTINUE ELSE PRINT
135 
136 %left BOOL_OR
137 %left BOOL_AND
138 %nonassoc BOOL_NOT
139 %nonassoc EQUALS LESS_EQ GREATER_EQ UNEQUALS LESS GREATER
140 %right <str> ASSIGN_OP
141 %left PLUS MINUS
142 %left MULTIPLY DIVIDE REMAINDER
143 %right EXPONENT
144 %nonassoc UMINUS
145 %nonassoc INCR DECR
146 
147 %type <lvalue>	named_expression
148 %type <node>	argument_list
149 %type <node>	alloc_macro
150 %type <node>	expression
151 %type <node>	function
152 %type <node>	function_header
153 %type <node>	input_item
154 %type <node>	opt_argument_list
155 %type <node>	opt_expression
156 %type <node>	opt_relational_expression
157 %type <node>	opt_statement
158 %type <node>	print_expression
159 %type <node>	print_expression_list
160 %type <node>	relational_expression
161 %type <node>	return_expression
162 %type <node>	semicolon_list
163 %type <node>	statement
164 %type <node>	statement_list
165 
166 %%
167 
168 program		: /* empty */
169 		| program input_item
170 		;
171 
172 input_item	: semicolon_list NEWLINE
173 			{
174 				emit($1);
175 				macro_char = reset_macro_char;
176 				putchar('\n');
177 				free_tree();
178 				st_has_continue = false;
179 			}
180 		| function
181 			{
182 				putchar('\n');
183 				free_tree();
184 				st_has_continue = false;
185 			}
186 		| error NEWLINE
187 			{
188 				yyerrok;
189 			}
190 		;
191 
192 semicolon_list	: /* empty */
193 			{
194 				$$ = cs("");
195 			}
196 		| statement
197 		| semicolon_list SEMICOLON statement
198 			{
199 				$$ = node($1, $3, END_NODE);
200 			}
201 		| semicolon_list SEMICOLON
202 		;
203 
204 statement_list	: /* empty */
205 			{
206 				$$ = cs("");
207 			}
208 		| statement
209 		| statement_list NEWLINE
210 		| statement_list NEWLINE statement
211 			{
212 				$$ = node($1, $3, END_NODE);
213 			}
214 		| statement_list SEMICOLON
215 		| statement_list SEMICOLON statement
216 			{
217 				$$ = node($1, $3, END_NODE);
218 			}
219 		;
220 
221 
222 opt_statement	: /* empty */
223 			{
224 				$$ = cs("");
225 			}
226 		| statement
227 		;
228 
229 statement	: expression
230 			{
231 				$$ = node($1, cs("ps."), END_NODE);
232 			}
233 		| named_expression ASSIGN_OP expression
234 			{
235 				if ($2[0] == '\0')
236 					$$ = node($3, cs($2), $1.store,
237 					    END_NODE);
238 				else
239 					$$ = node($1.load, $3, cs($2), $1.store,
240 					    END_NODE);
241 			}
242 		| STRING
243 			{
244 				$$ = node(cs("["), as($1),
245 				    cs("]P"), END_NODE);
246 			}
247 		| BREAK
248 			{
249 				if (breaksp == 0) {
250 					warning("break not in for or while");
251 					YYERROR;
252 				} else {
253 					$$ = node(
254 					    numnode(nesting -
255 						breakstack[breaksp-1]),
256 					    cs("Q"), END_NODE);
257 				}
258 			}
259 		| CONTINUE
260 			{
261 				if (breaksp == 0) {
262 					warning("continue not in for or while");
263 					YYERROR;
264 				} else {
265 					st_has_continue = true;
266 					$$ = node(numnode(nesting -
267 					    breakstack[breaksp-1] - 1),
268 					    cs("J"), END_NODE);
269 				}
270 			}
271 		| QUIT
272 			{
273 				putchar('q');
274 				fflush(stdout);
275 				exit(0);
276 			}
277 		| RETURN return_expression
278 			{
279 				if (nesting == 0) {
280 					warning("return must be in a function");
281 					YYERROR;
282 				}
283 				$$ = $2;
284 			}
285 		| FOR LPAR alloc_macro opt_expression SEMICOLON
286 		     opt_relational_expression SEMICOLON
287 		     opt_expression RPAR opt_statement pop_nesting
288 			{
289 				ssize_t n;
290 
291 				if (st_has_continue)
292 					n = node($10, cs("M"), $8, cs("s."),
293 					    $6, $3, END_NODE);
294 				else
295 					n = node($10, $8, cs("s."), $6, $3,
296 					    END_NODE);
297 
298 				emit_macro($3, n);
299 				$$ = node($4, cs("s."), $6, $3, cs(" "),
300 				    END_NODE);
301 			}
302 		| IF LPAR alloc_macro pop_nesting relational_expression RPAR
303 		      opt_statement
304 			{
305 				emit_macro($3, $7);
306 				$$ = node($5, $3, cs(" "), END_NODE);
307 			}
308 		| IF LPAR alloc_macro pop_nesting relational_expression RPAR
309 		      opt_statement ELSE alloc_macro pop_nesting opt_statement
310 			{
311 				emit_macro($3, $7);
312 				emit_macro($9, $11);
313 				$$ = node($5, $3, cs("e"), $9, cs(" "),
314 				    END_NODE);
315 			}
316 		| WHILE LPAR alloc_macro relational_expression RPAR
317 		      opt_statement pop_nesting
318 			{
319 				ssize_t n;
320 
321 				if (st_has_continue)
322 					n = node($6, cs("M"), $4, $3, END_NODE);
323 				else
324 					n = node($6, $4, $3, END_NODE);
325 				emit_macro($3, n);
326 				$$ = node($4, $3, cs(" "), END_NODE);
327 			}
328 		| LBRACE statement_list RBRACE
329 			{
330 				$$ = $2;
331 			}
332 		| PRINT print_expression_list
333 			{
334 				$$ = $2;
335 			}
336 		;
337 
338 alloc_macro	: /* empty */
339 			{
340 				$$ = cs(str_table[macro_char]);
341 				macro_char++;
342 				/* Do not use [, \ and ] */
343 				if (macro_char == '[')
344 					macro_char += 3;
345 				/* skip letters */
346 				else if (macro_char == 'a')
347 					macro_char = '{';
348 				else if (macro_char == ARRAY_CHAR)
349 					macro_char += 26;
350 				else if (macro_char == 255)
351 					fatal("program too big");
352 				if (breaksp == BREAKSTACK_SZ)
353 					fatal("nesting too deep");
354 				breakstack[breaksp++] = nesting++;
355 			}
356 		;
357 
358 pop_nesting	: /* empty */
359 			{
360 				breaksp--;
361 			}
362 		;
363 
364 function	: function_header opt_parameter_list RPAR opt_newline
365 		  LBRACE NEWLINE opt_auto_define_list
366 		  statement_list RBRACE
367 			{
368 				int n = node(prologue, $8, epilogue,
369 				    cs("0"), numnode(nesting),
370 				    cs("Q"), END_NODE);
371 				emit_macro($1, n);
372 				reset_macro_char = macro_char;
373 				nesting = 0;
374 				breaksp = 0;
375 			}
376 		;
377 
378 function_header : DEFINE LETTER LPAR
379 			{
380 				$$ = function_node($2);
381 				free($2);
382 				prologue = cs("");
383 				epilogue = cs("");
384 				nesting = 1;
385 				breaksp = 0;
386 				breakstack[breaksp] = 0;
387 			}
388 		;
389 
390 opt_newline	: /* empty */
391 		| NEWLINE
392 		;
393 
394 opt_parameter_list
395 		: /* empty */
396 		| parameter_list
397 		;
398 
399 
400 parameter_list	: LETTER
401 			{
402 				add_par(letter_node($1));
403 				free($1);
404 			}
405 		| LETTER LBRACKET RBRACKET
406 			{
407 				add_par(array_node($1));
408 				free($1);
409 			}
410 		| parameter_list COMMA LETTER
411 			{
412 				add_par(letter_node($3));
413 				free($3);
414 			}
415 		| parameter_list COMMA LETTER LBRACKET RBRACKET
416 			{
417 				add_par(array_node($3));
418 				free($3);
419 			}
420 		;
421 
422 
423 
424 opt_auto_define_list
425 		: /* empty */
426 		| AUTO define_list NEWLINE
427 		| AUTO define_list SEMICOLON
428 		;
429 
430 
431 define_list	: LETTER
432 			{
433 				add_local(letter_node($1));
434 				free($1);
435 			}
436 		| LETTER LBRACKET RBRACKET
437 			{
438 				add_local(array_node($1));
439 				free($1);
440 			}
441 		| define_list COMMA LETTER
442 			{
443 				add_local(letter_node($3));
444 				free($3);
445 			}
446 		| define_list COMMA LETTER LBRACKET RBRACKET
447 			{
448 				add_local(array_node($3));
449 				free($3);
450 			}
451 		;
452 
453 
454 opt_argument_list
455 		: /* empty */
456 			{
457 				$$ = cs("");
458 			}
459 		| argument_list
460 		;
461 
462 
463 argument_list	: expression
464 		| argument_list COMMA expression
465 			{
466 				$$ = node($1, $3, END_NODE);
467 			}
468 		| argument_list COMMA LETTER LBRACKET RBRACKET
469 			{
470 				$$ = node($1, cs("l"), array_node($3),
471 				    END_NODE);
472 				free($3);
473 			}
474 		;
475 
476 opt_relational_expression
477 		: /* empty */
478 			{
479 				$$ = cs(" 0 0=");
480 			}
481 		| relational_expression
482 		;
483 
484 relational_expression
485 		: expression EQUALS expression
486 			{
487 				$$ = node($1, $3, cs("="), END_NODE);
488 			}
489 		| expression UNEQUALS expression
490 			{
491 				$$ = node($1, $3, cs("!="), END_NODE);
492 			}
493 		| expression LESS expression
494 			{
495 				$$ = node($1, $3, cs(">"), END_NODE);
496 			}
497 		| expression LESS_EQ expression
498 			{
499 				$$ = node($1, $3, cs("!<"), END_NODE);
500 			}
501 		| expression GREATER expression
502 			{
503 				$$ = node($1, $3, cs("<"), END_NODE);
504 			}
505 		| expression GREATER_EQ expression
506 			{
507 				$$ = node($1, $3, cs("!>"), END_NODE);
508 			}
509 		| expression
510 			{
511 				$$ = node($1, cs(" 0!="), END_NODE);
512 			}
513 		;
514 
515 
516 return_expression
517 		: /* empty */
518 			{
519 				$$ = node(cs("0"), epilogue,
520 				    numnode(nesting), cs("Q"), END_NODE);
521 			}
522 		| expression
523 			{
524 				$$ = node($1, epilogue,
525 				    numnode(nesting), cs("Q"), END_NODE);
526 			}
527 		| LPAR RPAR
528 			{
529 				$$ = node(cs("0"), epilogue,
530 				    numnode(nesting), cs("Q"), END_NODE);
531 			}
532 		;
533 
534 
535 opt_expression : /* empty */
536 			{
537 				$$ = cs(" 0");
538 			}
539 		| expression
540 		;
541 
542 expression	: named_expression
543 			{
544 				$$ = node($1.load, END_NODE);
545 			}
546 		| DOT	{
547 				$$ = node(cs("l."), END_NODE);
548 			}
549 		| NUMBER
550 			{
551 				$$ = node(cs(" "), as($1), END_NODE);
552 			}
553 		| LPAR expression RPAR
554 			{
555 				$$ = $2;
556 			}
557 		| LETTER LPAR opt_argument_list RPAR
558 			{
559 				$$ = node($3, cs("l"),
560 				    function_node($1), cs("x"),
561 				    END_NODE);
562 				free($1);
563 			}
564 		| MINUS expression %prec UMINUS
565 			{
566 				$$ = node(cs(" 0"), $2, cs("-"),
567 				    END_NODE);
568 			}
569 		| expression PLUS expression
570 			{
571 				$$ = node($1, $3, cs("+"), END_NODE);
572 			}
573 		| expression MINUS expression
574 			{
575 				$$ = node($1, $3, cs("-"), END_NODE);
576 			}
577 		| expression MULTIPLY expression
578 			{
579 				$$ = node($1, $3, cs("*"), END_NODE);
580 			}
581 		| expression DIVIDE expression
582 			{
583 				$$ = node($1, $3, cs("/"), END_NODE);
584 			}
585 		| expression REMAINDER expression
586 			{
587 				$$ = node($1, $3, cs("%"), END_NODE);
588 			}
589 		| expression EXPONENT expression
590 			{
591 				$$ = node($1, $3, cs("^"), END_NODE);
592 			}
593 		| INCR named_expression
594 			{
595 				$$ = node($2.load, cs("1+d"), $2.store,
596 				    END_NODE);
597 			}
598 		| DECR named_expression
599 			{
600 				$$ = node($2.load, cs("1-d"),
601 				    $2.store, END_NODE);
602 			}
603 		| named_expression INCR
604 			{
605 				$$ = node($1.load, cs("d1+"),
606 				    $1.store, END_NODE);
607 			}
608 		| named_expression DECR
609 			{
610 				$$ = node($1.load, cs("d1-"),
611 				    $1.store, END_NODE);
612 			}
613 		| named_expression ASSIGN_OP expression
614 			{
615 				if ($2[0] == '\0')
616 					$$ = node($3, cs($2), cs("d"), $1.store,
617 					    END_NODE);
618 				else
619 					$$ = node($1.load, $3, cs($2), cs("d"),
620 					    $1.store, END_NODE);
621 			}
622 		| LENGTH LPAR expression RPAR
623 			{
624 				$$ = node($3, cs("Z"), END_NODE);
625 			}
626 		| SQRT LPAR expression RPAR
627 			{
628 				$$ = node($3, cs("v"), END_NODE);
629 			}
630 		| SCALE LPAR expression RPAR
631 			{
632 				$$ = node($3, cs("X"), END_NODE);
633 			}
634 		| BOOL_NOT expression
635 			{
636 				$$ = node($2, cs("N"), END_NODE);
637 			}
638 		| expression BOOL_AND alloc_macro pop_nesting expression
639 			{
640 				ssize_t n = node(cs("R"), $5, END_NODE);
641 				emit_macro($3, n);
642 				$$ = node($1, cs("d0!="), $3, END_NODE);
643 			}
644 		| expression BOOL_OR alloc_macro pop_nesting expression
645 			{
646 				ssize_t n = node(cs("R"), $5, END_NODE);
647 				emit_macro($3, n);
648 				$$ = node($1, cs("d0="), $3, END_NODE);
649 			}
650 		| expression EQUALS expression
651 			{
652 				$$ = node($1, $3, cs("G"), END_NODE);
653 			}
654 		| expression UNEQUALS expression
655 			{
656 				$$ = node($1, $3, cs("GN"), END_NODE);
657 			}
658 		| expression LESS expression
659 			{
660 				$$ = node($3, $1, cs("("), END_NODE);
661 			}
662 		| expression LESS_EQ expression
663 			{
664 				$$ = node($3, $1, cs("{"), END_NODE);
665 			}
666 		| expression GREATER expression
667 			{
668 				$$ = node($1, $3, cs("("), END_NODE);
669 			}
670 		| expression GREATER_EQ expression
671 			{
672 				$$ = node($1, $3, cs("{"), END_NODE);
673 			}
674 		;
675 
676 named_expression
677 		: LETTER
678 			{
679 				$$.load = node(cs("l"), letter_node($1),
680 				    END_NODE);
681 				$$.store = node(cs("s"), letter_node($1),
682 				    END_NODE);
683 				free($1);
684 			}
685 		| LETTER LBRACKET expression RBRACKET
686 			{
687 				$$.load = node($3, cs(";"),
688 				    array_node($1), END_NODE);
689 				$$.store = node($3, cs(":"),
690 				    array_node($1), END_NODE);
691 				free($1);
692 			}
693 		| SCALE
694 			{
695 				$$.load = cs("K");
696 				$$.store = cs("k");
697 			}
698 		| IBASE
699 			{
700 				$$.load = cs("I");
701 				$$.store = cs("i");
702 			}
703 		| OBASE
704 			{
705 				$$.load = cs("O");
706 				$$.store = cs("o");
707 			}
708 		;
709 
710 print_expression_list
711 		: print_expression
712 		| print_expression_list COMMA print_expression
713 			{
714 				$$ = node($1, $3, END_NODE);
715 			}
716 
717 print_expression
718 		: expression
719 			{
720 				$$ = node($1, cs("ds.n"), END_NODE);
721 			}
722 		| STRING
723 			{
724 				char *p = escape($1);
725 				$$ = node(cs("["), as(p), cs("]n"), END_NODE);
726 				free(p);
727 			}
728 %%
729 
730 
731 static void
732 grow(void)
733 {
734 	struct tree	*p;
735 	int		newsize;
736 
737 	if (current == instr_sz) {
738 		newsize = instr_sz * 2 + 1;
739 		p = realloc(instructions, newsize * sizeof(*p));
740 		if (p == NULL) {
741 			free(instructions);
742 			err(1, NULL);
743 		}
744 		instructions = p;
745 		instr_sz = newsize;
746 	}
747 }
748 
749 static ssize_t
750 cs(const char *str)
751 {
752 	grow();
753 	instructions[current].index = CONST_STRING;
754 	instructions[current].u.cstr = str;
755 	return current++;
756 }
757 
758 static ssize_t
759 as(const char *str)
760 {
761 	grow();
762 	instructions[current].index = ALLOC_STRING;
763 	instructions[current].u.astr = strdup(str);
764 	if (instructions[current].u.astr == NULL)
765 		err(1, NULL);
766 	return current++;
767 }
768 
769 static ssize_t
770 node(ssize_t arg, ...)
771 {
772 	va_list		ap;
773 	ssize_t		ret;
774 
775 	va_start(ap, arg);
776 
777 	ret = current;
778 	grow();
779 	instructions[current++].index = arg;
780 
781 	do {
782 		arg = va_arg(ap, ssize_t);
783 		grow();
784 		instructions[current++].index = arg;
785 	} while (arg != END_NODE);
786 
787 	va_end(ap);
788 	return ret;
789 }
790 
791 static void
792 emit(ssize_t i)
793 {
794 	if (instructions[i].index >= 0)
795 		while (instructions[i].index != END_NODE)
796 			emit(instructions[i++].index);
797 	else
798 		fputs(instructions[i].u.cstr, stdout);
799 }
800 
801 static void
802 emit_macro(int node, ssize_t code)
803 {
804 	putchar('[');
805 	emit(code);
806 	printf("]s%s\n", instructions[node].u.cstr);
807 	nesting--;
808 }
809 
810 static void
811 free_tree(void)
812 {
813 	size_t i;
814 
815 	for (i = 0; i < current; i++)
816 		if (instructions[i].index == ALLOC_STRING)
817 			free(instructions[i].u.astr);
818 	current = 0;
819 }
820 
821 static ssize_t
822 numnode(int num)
823 {
824 	const char *p;
825 
826 	if (num < 10)
827 		p = str_table['0' + num];
828 	else if (num < 16)
829 		p = str_table['A' - 10 + num];
830 	else
831 		errx(1, "internal error: break num > 15");
832 	return node(cs(" "), cs(p), END_NODE);
833 }
834 
835 
836 static ssize_t
837 lookup(char * str, size_t len, char type)
838 {
839 	ENTRY	entry, *found;
840 	u_short	num;
841 	u_char	*p;
842 
843 	/* The scanner allocated an extra byte already */
844 	if (str[len-1] != type) {
845 		str[len] = type;
846 		str[len+1] = '\0';
847 	}
848 	entry.key = str;
849 	found = hsearch(entry, FIND);
850 	if (found == NULL) {
851 		if (var_count == MAX_VARIABLES)
852 			errx(1, "too many variables");
853 		p = malloc(4);
854 		if (p == NULL)
855 			err(1, NULL);
856 		num = var_count++;
857 		p[0] = 255;
858 		p[1] = ENCODE(num / VAR_BASE + 1);
859 		p[2] = ENCODE(num % VAR_BASE + 1);
860 		p[3] = '\0';
861 
862 		entry.data = (char *)p;
863 		entry.key = strdup(str);
864 		if (entry.key == NULL)
865 			err(1, NULL);
866 		found = hsearch(entry, ENTER);
867 		if (found == NULL)
868 			err(1, NULL);
869 	}
870 	return cs(found->data);
871 }
872 
873 static ssize_t
874 letter_node(char *str)
875 {
876 	size_t len;
877 
878 	len = strlen(str);
879 	if (len == 1 && str[0] != '_')
880 		return cs(str_table[(int)str[0]]);
881 	else
882 		return lookup(str, len, 'L');
883 }
884 
885 static ssize_t
886 array_node(char *str)
887 {
888 	size_t len;
889 
890 	len = strlen(str);
891 	if (len == 1 && str[0] != '_')
892 		return cs(str_table[(int)str[0] - 'a' + ARRAY_CHAR]);
893 	else
894 		return lookup(str, len, 'A');
895 }
896 
897 static ssize_t
898 function_node(char *str)
899 {
900 	size_t len;
901 
902 	len = strlen(str);
903 	if (len == 1 && str[0] != '_')
904 		return cs(str_table[(int)str[0] - 'a' + FUNC_CHAR]);
905 	else
906 		return lookup(str, len, 'F');
907 }
908 
909 static void
910 add_par(ssize_t n)
911 {
912 	prologue = node(cs("S"), n, prologue, END_NODE);
913 	epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE);
914 }
915 
916 static void
917 add_local(ssize_t n)
918 {
919 	prologue = node(cs("0S"), n, prologue, END_NODE);
920 	epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE);
921 }
922 
923 int
924 yywrap(void)
925 {
926 	if (fileindex < sargc) {
927 		filename = sargv[fileindex++];
928 		yyin = fopen(filename, "r");
929 		lineno = 1;
930 		if (yyin == NULL)
931 			err(1, "cannot open %s", filename);
932 		return 0;
933 	} else if (fileindex == sargc) {
934 		fileindex++;
935 		yyin = stdin;
936 		lineno = 1;
937 		filename = "stdin";
938 		return 0;
939 	}
940 	return 1;
941 }
942 
943 void
944 yyerror(char *s)
945 {
946 	char	*str, *p;
947 
948 	if (isspace(yytext[0]) || !isprint(yytext[0]))
949 		asprintf(&str, "%s: %s:%d: %s: ascii char 0x%x unexpected",
950 		    __progname, filename, lineno, s, yytext[0]);
951 	else
952 		asprintf(&str, "%s: %s:%d: %s: %s unexpected",
953 		    __progname, filename, lineno, s, yytext);
954 	if (str == NULL)
955 		err(1, NULL);
956 
957 	fputs("c[", stdout);
958 	for (p = str; *p != '\0'; p++) {
959 		if (*p == '[' || *p == ']' || *p =='\\')
960 			putchar('\\');
961 		putchar(*p);
962 	}
963 	fputs("]pc\n", stdout);
964 	free(str);
965 }
966 
967 void
968 fatal(const char *s)
969 {
970 	errx(1, "%s:%d: %s", filename, lineno, s);
971 }
972 
973 static void
974 warning(const char *s)
975 {
976 	warnx("%s:%d: %s", filename, lineno, s);
977 }
978 
979 static void
980 init(void)
981 {
982 	int i;
983 
984 	for (i = 0; i < UCHAR_MAX; i++) {
985 		str_table[i][0] = i;
986 		str_table[i][1] = '\0';
987 	}
988 	if (hcreate(1 << 16) == 0)
989 		err(1, NULL);
990 }
991 
992 
993 static void
994 usage(void)
995 {
996 	fprintf(stderr, "%s: usage: [-cl] [file ...]\n", __progname);
997 	exit(1);
998 }
999 
1000 static char *
1001 escape(const char *str)
1002 {
1003 	char *ret, *p;
1004 
1005 	ret = malloc(strlen(str) + 1);
1006 	if (ret == NULL)
1007 		err(1, NULL);
1008 
1009 	p = ret;
1010 	while (*str != '\0') {
1011 		/*
1012 		 * We get _escaped_ strings here. Single backslashes are
1013 		 * already converted to double backslashes
1014 		 */
1015 		if (*str == '\\') {
1016 			if (*++str == '\\') {
1017 				switch (*++str) {
1018 				case 'a':
1019 					*p++ = '\a';
1020 					break;
1021 				case 'b':
1022 					*p++ = '\b';
1023 					break;
1024 				case 'f':
1025 					*p++ = '\f';
1026 					break;
1027 				case 'n':
1028 					*p++ = '\n';
1029 					break;
1030 				case 'q':
1031 					*p++ = '"';
1032 					break;
1033 				case 'r':
1034 					*p++ = '\r';
1035 					break;
1036 				case 't':
1037 					*p++ = '\t';
1038 					break;
1039 				case '\\':
1040 					*p++ = '\\';
1041 					break;
1042 				}
1043 				str++;
1044 			} else {
1045 				*p++ = '\\';
1046 				*p++ = *str++;
1047 			}
1048 		} else
1049 			*p++ = *str++;
1050 	}
1051 	*p = '\0';
1052 	return ret;
1053 }
1054 
1055 int
1056 main(int argc, char *argv[])
1057 {
1058 	int	i, ch, ret;
1059 	int	p[2];
1060 
1061 	init();
1062 	setlinebuf(stdout);
1063 
1064 	sargv = malloc(argc * sizeof(char *));
1065 	if (sargv == NULL)
1066 		err(1, NULL);
1067 
1068 	/* The d debug option is 4.4 BSD dc(1) compatible */
1069 	while ((ch = getopt(argc, argv, "cdl")) != -1) {
1070 		switch (ch) {
1071 		case 'c':
1072 		case 'd':
1073 			do_fork = false;
1074 			break;
1075 		case 'l':
1076 			sargv[sargc++] = _PATH_LIBB;
1077 			break;
1078 		default:
1079 			usage();
1080 		}
1081 	}
1082 
1083 	argc -= optind;
1084 	argv += optind;
1085 
1086 	for (i = 0; i < argc; i++)
1087 		sargv[sargc++] = argv[i];
1088 
1089 	if (do_fork) {
1090 		if (pipe(p) == -1)
1091 			err(1, "cannot create pipe");
1092 		ret = fork();
1093 		if (ret == -1)
1094 			err(1, "cannot fork");
1095 		else if (ret == 0) {
1096 			close(STDOUT_FILENO);
1097 			dup(p[1]);
1098 			close(p[0]);
1099 			close(p[1]);
1100 		} else {
1101 			close(STDIN_FILENO);
1102 			dup(p[0]);
1103 			close(p[0]);
1104 			close(p[1]);
1105 			execl(_PATH_DC, "dc", "-x", (char *)NULL);
1106 			err(1, "cannot find dc");
1107 		}
1108 	}
1109 	signal(SIGINT, abort_line);
1110 	yywrap();
1111 	return yyparse();
1112 }
1113