3 * $OpenBSD: bc.y,v 1.32 2006/05/18 05:49:53 otto Exp $
7 * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net>
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * This implementation of bc(1) uses concepts from the original 4.4
24 * BSD bc(1). The code itself is a complete rewrite, based on the
25 * Posix defined bc(1) grammar. Other differences include type safe
26 * usage of pointers to build the tree of emitted code, typed yacc
27 * rule values, dynamic allocation of all data structures and a
28 * completely rewritten lexical analyzer using lex(1).
30 * Some effort has been made to make sure that the generated code is
31 * the same as the code generated by the older version, to provide
32 * easy regression testing.
35 #include <sys/types.h>
52 #include "pathnames.h"
54 #define END_NODE ((ssize_t) -1)
55 #define CONST_STRING ((ssize_t) -2)
56 #define ALLOC_STRING ((ssize_t) -3)
75 static void grow
(void);
76 static ssize_t cs
(const char *);
77 static ssize_t as
(const char *);
78 static ssize_t node
(ssize_t
, ...
);
79 static void emit
(ssize_t
);
80 static void emit_macro
(int, ssize_t
);
81 static void free_tree
(void);
82 static ssize_t numnode
(int);
83 static ssize_t lookup
(char *, size_t, char);
84 static ssize_t letter_node
(char *);
85 static ssize_t array_node
(char *);
86 static ssize_t function_node
(char *);
88 static void add_par
(ssize_t
);
89 static void add_local
(ssize_t
);
90 static void warning
(const char *);
91 static void init
(void);
92 static __dead2
void usage
(void);
93 static char *escape
(const char *);
95 static ssize_t instr_sz
= 0;
96 static struct tree
*instructions
= NULL
;
97 static ssize_t current
= 0;
98 static int macro_char
= '0';
99 static int reset_macro_char
= '0';
100 static int nesting
= 0;
101 static int breakstack
[16];
102 static int breaksp
= 0;
103 static ssize_t prologue
;
104 static ssize_t epilogue
;
105 static bool st_has_continue
;
106 static char str_table
[UCHAR_MAX
][2];
107 static bool do_fork
= true
;
108 static u_short var_count
;
111 extern
char *__progname
;
113 #define BREAKSTACK_SZ (sizeof(breakstack)/sizeof(breakstack[0]))
115 /* These values are 4.4BSD bc compatible */
116 #define FUNC_CHAR 0x01
117 #define ARRAY_CHAR 0xa1
119 /* Skip '\0', [, \ and ] */
120 #define ENCODE(c) ((c) < '[' ? (c) : (c) + 3);
121 #define VAR_BASE (256-4)
122 #define MAX_VARIABLES (VAR_BASE * VAR_BASE)
130 struct lvalue lvalue
;
135 %token COMMA SEMICOLON LPAR RPAR LBRACE RBRACE LBRACKET RBRACKET DOT
138 %token
<str
> NUMBER STRING
139 %token DEFINE BREAK QUIT LENGTH
140 %token RETURN FOR IF WHILE SQRT
141 %token SCALE IBASE OBASE AUTO
142 %token CONTINUE ELSE PRINT
147 %nonassoc EQUALS LESS_EQ GREATER_EQ UNEQUALS LESS GREATER
148 %right
<str
> ASSIGN_OP
150 %left MULTIPLY DIVIDE REMAINDER
155 %type
<lvalue
> named_expression
156 %type
<node
> argument_list
157 %type
<node
> alloc_macro
158 %type
<node
> expression
159 %type
<node
> function
160 %type
<node
> function_header
161 %type
<node
> input_item
162 %type
<node
> opt_argument_list
163 %type
<node
> opt_expression
164 %type
<node
> opt_relational_expression
165 %type
<node
> opt_statement
166 %type
<node
> print_expression
167 %type
<node
> print_expression_list
168 %type
<node
> relational_expression
169 %type
<node
> return_expression
170 %type
<node
> semicolon_list
171 %type
<node
> statement
172 %type
<node
> statement_list
176 program
: /* empty */
180 input_item
: semicolon_list NEWLINE
183 macro_char
= reset_macro_char
;
186 st_has_continue
= false
;
192 st_has_continue
= false
;
204 semicolon_list
: /* empty */
209 | semicolon_list SEMICOLON statement
211 $$
= node
($1, $3, END_NODE
);
213 | semicolon_list SEMICOLON
216 statement_list
: /* empty */
221 | statement_list NEWLINE
222 | statement_list NEWLINE statement
224 $$
= node
($1, $3, END_NODE
);
226 | statement_list SEMICOLON
227 | statement_list SEMICOLON statement
229 $$
= node
($1, $3, END_NODE
);
234 opt_statement
: /* empty */
241 statement
: expression
243 $$
= node
($1, cs
("ps."), END_NODE
);
245 | named_expression ASSIGN_OP expression
248 $$
= node
($3, cs
($2), $1.store
,
251 $$
= node
($1.load
, $3, cs
($2), $1.store
,
256 $$
= node
(cs
("["), as
($1),
262 warning
("break not in for or while");
267 breakstack
[breaksp
-1]),
274 warning
("continue not in for or while");
277 st_has_continue
= true
;
278 $$
= node
(numnode
(nesting
-
279 breakstack
[breaksp
-1] - 1),
290 sigprocmask
(SIG_BLOCK
, NULL
, &mask
);
295 | RETURN return_expression
298 warning
("return must be in a function");
303 | FOR LPAR alloc_macro opt_expression SEMICOLON
304 opt_relational_expression SEMICOLON
305 opt_expression RPAR opt_statement pop_nesting
310 n
= node
($10, cs
("M"), $8, cs
("s."),
313 n
= node
($10, $8, cs
("s."), $6, $3,
317 $$
= node
($4, cs
("s."), $6, $3, cs
(" "),
320 | IF LPAR alloc_macro pop_nesting relational_expression RPAR
324 $$
= node
($5, $3, cs
(" "), END_NODE
);
326 | IF LPAR alloc_macro pop_nesting relational_expression RPAR
327 opt_statement ELSE alloc_macro pop_nesting opt_statement
331 $$
= node
($5, $3, cs
("e"), $9, cs
(" "),
334 | WHILE LPAR alloc_macro relational_expression RPAR
335 opt_statement pop_nesting
340 n
= node
($6, cs
("M"), $4, $3, END_NODE
);
342 n
= node
($6, $4, $3, END_NODE
);
344 $$
= node
($4, $3, cs
(" "), END_NODE
);
346 | LBRACE statement_list RBRACE
350 | PRINT print_expression_list
356 alloc_macro
: /* empty */
358 $$
= cs
(str_table
[macro_char
]);
360 /* Do not use [, \ and ] */
361 if
(macro_char
== '[')
364 else if
(macro_char
== 'a')
366 else if
(macro_char
== ARRAY_CHAR
)
368 else if
(macro_char
== 255)
369 fatal
("program too big");
370 if
(breaksp
== BREAKSTACK_SZ
)
371 fatal
("nesting too deep");
372 breakstack
[breaksp
++] = nesting
++;
376 pop_nesting
: /* empty */
382 function
: function_header opt_parameter_list RPAR opt_newline
383 LBRACE NEWLINE opt_auto_define_list
384 statement_list RBRACE
386 int n
= node
(prologue
, $8, epilogue
,
387 cs
("0"), numnode
(nesting
),
390 reset_macro_char
= macro_char
;
396 function_header
: DEFINE LETTER LPAR
398 $$
= function_node
($2);
404 breakstack
[breaksp
] = 0;
408 opt_newline
: /* empty */
418 parameter_list
: LETTER
420 add_par
(letter_node
($1));
423 | LETTER LBRACKET RBRACKET
425 add_par
(array_node
($1));
428 | parameter_list COMMA LETTER
430 add_par
(letter_node
($3));
433 | parameter_list COMMA LETTER LBRACKET RBRACKET
435 add_par
(array_node
($3));
444 | AUTO define_list NEWLINE
445 | AUTO define_list SEMICOLON
451 add_local
(letter_node
($1));
454 | LETTER LBRACKET RBRACKET
456 add_local
(array_node
($1));
459 | define_list COMMA LETTER
461 add_local
(letter_node
($3));
464 | define_list COMMA LETTER LBRACKET RBRACKET
466 add_local
(array_node
($3));
481 argument_list
: expression
482 | argument_list COMMA expression
484 $$
= node
($1, $3, END_NODE
);
486 | argument_list COMMA LETTER LBRACKET RBRACKET
488 $$
= node
($1, cs
("l"), array_node
($3),
494 opt_relational_expression
499 | relational_expression
502 relational_expression
503 : expression EQUALS expression
505 $$
= node
($1, $3, cs
("="), END_NODE
);
507 | expression UNEQUALS expression
509 $$
= node
($1, $3, cs
("!="), END_NODE
);
511 | expression LESS expression
513 $$
= node
($1, $3, cs
(">"), END_NODE
);
515 | expression LESS_EQ expression
517 $$
= node
($1, $3, cs
("!<"), END_NODE
);
519 | expression GREATER expression
521 $$
= node
($1, $3, cs
("<"), END_NODE
);
523 | expression GREATER_EQ expression
525 $$
= node
($1, $3, cs
("!>"), END_NODE
);
529 $$
= node
($1, cs
(" 0!="), END_NODE
);
537 $$
= node
(cs
("0"), epilogue
,
538 numnode
(nesting
), cs
("Q"), END_NODE
);
542 $$
= node
($1, epilogue
,
543 numnode
(nesting
), cs
("Q"), END_NODE
);
547 $$
= node
(cs
("0"), epilogue
,
548 numnode
(nesting
), cs
("Q"), END_NODE
);
553 opt_expression
: /* empty */
560 expression
: named_expression
562 $$
= node
($1.load
, END_NODE
);
565 $$
= node
(cs
("l."), END_NODE
);
569 $$
= node
(cs
(" "), as
($1), END_NODE
);
571 | LPAR expression RPAR
575 | LETTER LPAR opt_argument_list RPAR
577 $$
= node
($3, cs
("l"),
578 function_node
($1), cs
("x"),
582 | MINUS expression %prec UMINUS
584 $$
= node
(cs
(" 0"), $2, cs
("-"),
587 | expression PLUS expression
589 $$
= node
($1, $3, cs
("+"), END_NODE
);
591 | expression MINUS expression
593 $$
= node
($1, $3, cs
("-"), END_NODE
);
595 | expression MULTIPLY expression
597 $$
= node
($1, $3, cs
("*"), END_NODE
);
599 | expression DIVIDE expression
601 $$
= node
($1, $3, cs
("/"), END_NODE
);
603 | expression REMAINDER expression
605 $$
= node
($1, $3, cs
("%"), END_NODE
);
607 | expression EXPONENT expression
609 $$
= node
($1, $3, cs
("^"), END_NODE
);
611 | INCR named_expression
613 $$
= node
($2.load
, cs
("1+d"), $2.store
,
616 | DECR named_expression
618 $$
= node
($2.load
, cs
("1-d"),
621 | named_expression INCR
623 $$
= node
($1.load
, cs
("d1+"),
626 | named_expression DECR
628 $$
= node
($1.load
, cs
("d1-"),
631 | named_expression ASSIGN_OP expression
634 $$
= node
($3, cs
($2), cs
("d"), $1.store
,
637 $$
= node
($1.load
, $3, cs
($2), cs
("d"),
640 | LENGTH LPAR expression RPAR
642 $$
= node
($3, cs
("Z"), END_NODE
);
644 | SQRT LPAR expression RPAR
646 $$
= node
($3, cs
("v"), END_NODE
);
648 | SCALE LPAR expression RPAR
650 $$
= node
($3, cs
("X"), END_NODE
);
652 | BOOL_NOT expression
654 $$
= node
($2, cs
("N"), END_NODE
);
656 | expression BOOL_AND alloc_macro pop_nesting expression
658 ssize_t n
= node
(cs
("R"), $5, END_NODE
);
660 $$
= node
($1, cs
("d0!="), $3, END_NODE
);
662 | expression BOOL_OR alloc_macro pop_nesting expression
664 ssize_t n
= node
(cs
("R"), $5, END_NODE
);
666 $$
= node
($1, cs
("d0="), $3, END_NODE
);
668 | expression EQUALS expression
670 $$
= node
($1, $3, cs
("G"), END_NODE
);
672 | expression UNEQUALS expression
674 $$
= node
($1, $3, cs
("GN"), END_NODE
);
676 | expression LESS expression
678 $$
= node
($3, $1, cs
("("), END_NODE
);
680 | expression LESS_EQ expression
682 $$
= node
($3, $1, cs
("{"), END_NODE
);
684 | expression GREATER expression
686 $$
= node
($1, $3, cs
("("), END_NODE
);
688 | expression GREATER_EQ expression
690 $$
= node
($1, $3, cs
("{"), END_NODE
);
697 $$.load
= node
(cs
("l"), letter_node
($1),
699 $$.store
= node
(cs
("s"), letter_node
($1),
703 | LETTER LBRACKET expression RBRACKET
705 $$.load
= node
($3, cs
(";"),
706 array_node
($1), END_NODE
);
707 $$.store
= node
($3, cs
(":"),
708 array_node
($1), END_NODE
);
728 print_expression_list
730 | print_expression_list COMMA print_expression
732 $$
= node
($1, $3, END_NODE
);
738 $$
= node
($1, cs
("ds.n"), END_NODE
);
742 char *p
= escape
($1);
743 $$
= node
(cs
("["), as
(p
), cs
("]n"), END_NODE
);
755 if
(current
== instr_sz
) {
756 newsize
= instr_sz
* 2 + 1;
757 p
= realloc
(instructions
, newsize
* sizeof
(*p
));
771 instructions
[current
].index
= CONST_STRING
;
772 instructions
[current
].u.cstr
= str
;
780 instructions
[current
].index
= ALLOC_STRING
;
781 instructions
[current
].u.astr
= strdup
(str
);
782 if
(instructions
[current
].u.astr
== NULL
)
788 node
(ssize_t arg
, ...
)
797 instructions
[current
++].index
= arg
;
800 arg
= va_arg
(ap
, ssize_t
);
802 instructions
[current
++].index
= arg
;
803 } while
(arg
!= END_NODE
);
812 if
(instructions
[i
].index
>= 0)
813 while
(instructions
[i
].index
!= END_NODE
)
814 emit
(instructions
[i
++].index
);
816 fputs
(instructions
[i
].u.cstr
, stdout
);
820 emit_macro
(int node
, ssize_t code
)
824 printf
("]s%s\n", instructions
[node
].u.cstr
);
833 for
(i
= 0; i
< current
; i
++)
834 if
(instructions
[i
].index
== ALLOC_STRING
)
835 free
(instructions
[i
].u.astr
);
845 p
= str_table
['0' + num
];
847 p
= str_table
['A' - 10 + num
];
849 errx
(1, "internal error: break num > 15");
850 return node
(cs
(" "), cs
(p
), END_NODE
);
855 lookup
(char * str
, size_t len
, char type
)
861 /* The scanner allocated an extra byte already */
862 if
(str
[len
-1] != type
) {
867 found
= hsearch
(entry
, FIND
);
869 if
(var_count
== MAX_VARIABLES
)
870 errx
(1, "too many variables");
876 p
[1] = ENCODE
(num
/ VAR_BASE
+ 1);
877 p
[2] = ENCODE
(num % VAR_BASE
+ 1);
880 entry.data
= (char *)p
;
881 entry.key
= strdup
(str
);
882 if
(entry.key
== NULL
)
884 found
= hsearch
(entry
, ENTER
);
888 return cs
(found
->data
);
892 letter_node
(char *str
)
897 if
(len
== 1 && str
[0] != '_')
898 return cs
(str_table
[(int)str
[0]]);
900 return lookup
(str
, len
, 'L');
904 array_node
(char *str
)
909 if
(len
== 1 && str
[0] != '_')
910 return cs
(str_table
[(int)str
[0] - 'a' + ARRAY_CHAR
]);
912 return lookup
(str
, len
, 'A');
916 function_node
(char *str
)
921 if
(len
== 1 && str
[0] != '_')
922 return cs
(str_table
[(int)str
[0] - 'a' + FUNC_CHAR
]);
924 return lookup
(str
, len
, 'F');
930 prologue
= node
(cs
("S"), n
, prologue
, END_NODE
);
931 epilogue
= node
(epilogue
, cs
("L"), n
, cs
("s."), END_NODE
);
937 prologue
= node
(cs
("0S"), n
, prologue
, END_NODE
);
938 epilogue
= node
(epilogue
, cs
("L"), n
, cs
("s."), END_NODE
);
947 if
(yyin
!= NULL
&& feof
(yyin
))
948 n
= asprintf
(&str
, "%s: %s:%d: %s: unexpected EOF",
949 __progname
, filename
, lineno
, s
);
950 else if
(isspace
(yytext
[0]) ||
!isprint
(yytext
[0]))
952 "%s: %s:%d: %s: ascii char 0x%02x unexpected",
953 __progname
, filename
, lineno
, s
, yytext
[0]);
955 n
= asprintf
(&str
, "%s: %s:%d: %s: %s unexpected",
956 __progname
, filename
, lineno
, s
, yytext
);
961 for
(p
= str
; *p
!= '\0'; p
++) {
962 if
(*p
== '[' ||
*p
== ']' ||
*p
=='\\')
966 fputs
("]pc\n", stdout
);
973 errx
(1, "%s:%d: %s", filename
, lineno
, s
);
977 warning
(const char *s
)
979 warnx
("%s:%d: %s", filename
, lineno
, s
);
987 for
(i
= 0; i
< UCHAR_MAX
; i
++) {
989 str_table
[i
][1] = '\0';
991 if
(hcreate
(1 << 16) == 0)
999 fprintf
(stderr
, "usage: %s [-cl] [-e expression] [file ...]\n",
1005 escape
(const char *str
)
1009 ret
= malloc
(strlen
(str
) + 1);
1014 while
(*str
!= '\0') {
1016 * We get _escaped_ strings here. Single backslashes are
1017 * already converted to double backslashes
1020 if
(*++str
== '\\') {
1067 pid
= waitpid
(dc
, &status
, WCONTINUED
);
1073 if
(WIFEXITED
(status
) || WIFSIGNALED
(status
))
1087 main
(int argc
, char *argv
[])
1096 sargv
= malloc
(argc
* sizeof
(char *));
1100 if
((cmdexpr
= strdup
("")) == NULL
)
1102 /* The d debug option is 4.4 BSD bc(1) compatible */
1103 while
((ch
= getopt
(argc
, argv
, "cde:l")) != -1) {
1111 if
(asprintf
(&cmdexpr
, "%s%s\n", cmdexpr
, optarg
) == -1)
1116 sargv
[sargc
++] = _PATH_LIBB
;
1126 interactive
= isatty
(STDIN_FILENO
);
1127 for
(i
= 0; i
< argc
; i
++)
1128 sargv
[sargc
++] = argv
[i
];
1132 err
(1, "cannot create pipe");
1135 err
(1, "cannot fork");
1137 signal
(SIGCHLD
, sigchld
);
1138 close
(STDOUT_FILENO
);
1143 close
(STDIN_FILENO
);
1147 execl
(_PATH_DC
, "dc", "-x", NULL
);
1148 err
(1, "cannot find dc");
1152 el
= el_init
("bc", stdin
, stderr
, stderr
);
1153 hist
= history_init
();
1154 history
(hist
, &he
, H_SETSIZE
, 100);
1155 el_set
(el
, EL_HIST
, history
, hist
);
1156 el_set
(el
, EL_EDITOR
, "emacs");
1157 el_set
(el
, EL_SIGNAL
, 1);
1158 el_set
(el
, EL_PROMPT
, dummy_prompt
);
1159 el_source
(el
, NULL
);