bc.y revision 7c478bd95313f5f23a4c958a745db2134aa03244
%{
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
%}
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
%{
#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.9 */
%}
%{
#include <stdio.h>
#include <stdarg.h>
#include <limits.h>
#include <libintl.h>
#include <locale.h>
#include <signal.h>
static int *getout(int);
static int *bundle(int, ...);
static void usage(void);
%}
%union {
int *iptr;
char *cptr;
int cc;
}
%start start;
%type <iptr> stat, def, slist, dlets, e
%type <iptr> slist, re, fprefix, cargs, eora, cons, constant, lora
%right '='
%left '+' '-'
%left '*' '/' '%'
%right '^'
%left UMINUS
%token <cptr> LETTER
%type <cptr> EQOP, CRS
%token <cc> DIGIT, SQRT, LENGTH, _IF, FFF, EQ
%token <cc> _WHILE _FOR NE LE GE INCR DECR
%token <cc> _RETURN _BREAK _DEFINE BASE OBASE SCALE
%token <cc> EQPL EQMI EQMUL EQDIV EQREM EQEXP
%token <cptr> _AUTO DOT
%token <cc> QSTR
%{
#define STRING_SIZE (BC_STRING_MAX + 3) /* string plus quotes */
/* plus NULL */
FILE *in;
char cary[LINE_MAX+1];
char *cp = { cary };
char *cpend = &cary[LINE_MAX]; /* last address (not the null char) */
char string[STRING_SIZE];
char *str = { string };
int crs = '0';
int rcrs = '0'; /* reset crs */
int bindx = 0;
int lev = 0; /* current scope level */
int ln; /* line number of current file */
int *ttp;
char *ss; /* current input source */
int bstack[10] = { 0 };
char *numb[15] = {
" 0", " 1", " 2", " 3", " 4", " 5",
" 6", " 7", " 8", " 9", " 10", " 11",
" 12", " 13", " 14"
};
int *pre, *post;
int interact = 0; /* talking to a tty? */
%}
%%
start :
| start stat tail
= {
output($2);
}
| start def dargs ')' '{' dlist slist '}'
= {
ttp = bundle(6, pre, $7, post, "0", numb[lev], "Q");
conout(ttp, $2);
rcrs = crs;
output("");
lev = bindx = 0;
}
;
dlist : tail
| dlist _AUTO dlets tail
;
stat : e
= bundle(2, $1, "ps.");
|
= bundle(1, "");
| QSTR
= bundle(3, "[", $1, "]P");
| LETTER '=' e
= bundle(3, $3, "s", $1);
| LETTER '[' e ']' '=' e
= bundle(4, $6, $3, ":", geta($1));
| LETTER EQOP e
= bundle(6, "l", $1, $3, $2, "s", $1);
| LETTER '[' e ']' EQOP e
= bundle(8, $3, ";", geta($1), $6, $5, $3, ":", geta($1));
| _BREAK
= bundle(2, numb[lev-bstack[bindx-1]], "Q");
| _RETURN '(' e ')'
= bundle(4, $3, post, numb[lev], "Q");
| _RETURN '(' ')'
= bundle(4, "0", post, numb[lev], "Q");
| _RETURN
= bundle(4, "0", post, numb[lev], "Q");
| SCALE '=' e
= bundle(2, $3, "k");
| SCALE EQOP e
= bundle(4, "K", $3, $2, "k");
| BASE '=' e
= bundle(2, $3, "i");
| BASE EQOP e
= bundle(4, "I", $3, $2, "i");
| OBASE '=' e
= bundle(2, $3, "o");
| OBASE EQOP e
= bundle(4, "O", $3, $2, "o");
| '{' slist '}'
= {
$$ = $2;
}
| FFF
= bundle(1, "fY");
| error
= bundle(1, "c");
| _IF CRS BLEV '(' re ')' stat
= {
conout($7, $2);
bundle(3, $5, $2, " ");
}
| _WHILE CRS '(' re ')' stat BLEV
= {
bundle(3, $6, $4, $2);
conout($$, $2);
bundle(3, $4, $2, " ");
}
| fprefix CRS re ';' e ')' stat BLEV
= {
bundle(5, $7, $5, "s.", $3, $2);
conout($$, $2);
bundle(5, $1, "s.", $3, $2, " ");
}
| '~' LETTER '=' e
= bundle(3, $4, "S", $2);
;
EQOP : EQPL
= {
$$ = "+";
}
| EQMI
= {
$$ = "-";
}
| EQMUL
= {
$$ = "*";
}
| EQDIV
= {
$$ = "/";
}
| EQREM
= {
$$ = "%%";
}
| EQEXP
= {
$$ = "^";
}
;
fprefix : _FOR '(' e ';'
= {
$$ = $3;
}
;
BLEV :
= --bindx;
;
slist : stat
| slist tail stat
= bundle(2, $1, $3);
;
tail : '\n'
= {
ln++;
}
| ';'
;
re : e EQ e
= {
$$ = bundle(3, $1, $3, "=");
}
| e '<' e
= bundle(3, $1, $3, ">");
| e '>' e
= bundle(3, $1, $3, "<");
| e NE e
= bundle(3, $1, $3, "!=");
| e GE e
= bundle(3, $1, $3, "!>");
| e LE e
= bundle(3, $1, $3, "!<");
| e
= bundle(2, $1, " 0!=");
;
e : e '+' e
= bundle(3, $1, $3, "+");
| e '-' e
= bundle(3, $1, $3, "-");
| '-' e %prec UMINUS
= bundle(3, " 0", $2, "-");
| e '*' e
= bundle(3, $1, $3, "*");
| e '/' e
= bundle(3, $1, $3, "/");
| e '%' e
= bundle(3, $1, $3, "%%");
| e '^' e
= bundle(3, $1, $3, "^");
| LETTER '[' e ']'
= bundle(3, $3, ";", geta($1));
| LETTER INCR
= bundle(4, "l", $1, "d1+s", $1);
| INCR LETTER
= bundle(4, "l", $2, "1+ds", $2);
| DECR LETTER
= bundle(4, "l", $2, "1-ds", $2);
| LETTER DECR
= bundle(4, "l", $1, "d1-s", $1);
| LETTER '[' e ']' INCR
= bundle(7, $3, ";", geta($1), "d1+", $3, ":", geta($1));
| INCR LETTER '[' e ']'
= bundle(7, $4, ";", geta($2), "1+d", $4, ":", geta($2));
| LETTER '[' e ']' DECR
= bundle(7, $3, ";", geta($1), "d1-", $3, ":", geta($1));
| DECR LETTER '[' e ']'
= bundle(7, $4, ";", geta($2), "1-d", $4, ":", geta($2));
| SCALE INCR
= bundle(1, "Kd1+k");
| INCR SCALE
= bundle(1, "K1+dk");
| SCALE DECR
= bundle(1, "Kd1-k");
| DECR SCALE
= bundle(1, "K1-dk");
| BASE INCR
= bundle(1, "Id1+i");
| INCR BASE
= bundle(1, "I1+di");
| BASE DECR
= bundle(1, "Id1-i");
| DECR BASE
= bundle(1, "I1-di");
| OBASE INCR
= bundle(1, "Od1+o");
| INCR OBASE
= bundle(1, "O1+do");
| OBASE DECR
= bundle(1, "Od1-o");
| DECR OBASE
= bundle(1, "O1-do");
| LETTER '(' cargs ')'
= bundle(4, $3, "l", getf($1), "x");
| LETTER '(' ')'
= bundle(3, "l", getf($1), "x");
| cons
= bundle(2, " ", $1);
| DOT cons
= bundle(2, " .", $2);
| cons DOT cons
= bundle(4, " ", $1, ".", $3);
| cons DOT
= bundle(3, " ", $1, ".");
| DOT
= {
$<cptr>$ = "l.";
}
| LETTER
= bundle(2, "l", $1);
| LETTER '=' e
= bundle(3, $3, "ds", $1);
| LETTER EQOP e %prec '='
= bundle(6, "l", $1, $3, $2, "ds", $1);
| LETTER '[' e ']' '=' e
= bundle(5, $6, "d", $3, ":", geta($1));
| LETTER '[' e ']' EQOP e
= {
bundle(9, $3, ";", geta($1), $6, $5, "d", $3, ":",
geta($1));
}
| LENGTH '(' e ')'
= bundle(2, $3, "Z");
| SCALE '(' e ')'
= bundle(2, $3, "X"); /* must be before '(' e ')' */
| '(' e ')'
= {
$$ = $2;
}
| '?'
= bundle(1, "?");
| SQRT '(' e ')'
= bundle(2, $3, "v");
| '~' LETTER
= bundle(2, "L", $2);
| SCALE '=' e
= bundle(2, $3, "dk");
| SCALE EQOP e %prec '='
= bundle(4, "K", $3, $2, "dk");
| BASE '=' e
= bundle(2, $3, "di");
| BASE EQOP e %prec '='
= bundle(4, "I", $3, $2, "di");
| OBASE '=' e
= bundle(2, $3, "do");
| OBASE EQOP e %prec '='
= bundle(4, "O", $3, $2, "do");
| SCALE
= bundle(1, "K");
| BASE
= bundle(1, "I");
| OBASE
= bundle(1, "O");
;
cargs : eora
| cargs ',' eora
= bundle(2, $1, $3);
;
eora : e
| LETTER '[' ']'
= bundle(2, "l", geta($1));
;
cons : constant
= {
*cp++ = '\0';
}
constant: '_'
= {
checkbuffer();
$<cptr>$ = cp;
*cp++ = '_';
}
| DIGIT
= {
checkbuffer();
$<cptr>$ = cp;
*cp++ = $1;
}
| constant DIGIT
= {
checkbuffer();
*cp++ = $2;
}
;
CRS :
= {
checkbuffer();
$$ = cp;
*cp++ = crs++;
*cp++ = '\0';
if (crs == '[')
crs += 3;
if (crs == 'a')
crs = '{';
if (crs >= 0241) {
yyerror("program too big");
getout(1);
}
bstack[bindx++] = lev++;
}
;
def : _DEFINE LETTER '('
= {
$$ = getf($2);
pre = (int *)"";
post = (int *)"";
lev = 1;
bstack[bindx = 0] = 0;
}
;
dargs : /* empty */
| lora
= {
pp($1);
}
| dargs ',' lora
= {
pp($3);
}
;
dlets : lora
= tp($1);
| dlets ',' lora
= tp($3);
;
lora : LETTER
= {
$<cptr>$ = $1;
}
| LETTER '[' ']'
= {
$$ = geta($1);
}
;
%%
#define error 256
int peekc = -1;
int ifile; /* current index into sargv */
int sargc; /* size of sargv[] */
char **sargv; /* saved arg list without options */
char funtab[52] = {
01, 0, 02, 0, 03, 0, 04, 0, 05, 0, 06, 0, 07, 0,
010, 0, 011, 0, 012, 0, 013, 0, 014, 0, 015, 0, 016, 0, 017, 0,
020, 0, 021, 0, 022, 0, 023, 0, 024, 0, 025, 0, 026, 0, 027, 0,
030, 0, 031, 0, 032, 0
};
unsigned char atab[52] = {
0241, 0, 0242, 0, 0243, 0, 0244, 0, 0245, 0, 0246, 0, 0247, 0, 0250, 0,
0251, 0, 0252, 0, 0253, 0, 0254, 0, 0255, 0, 0256, 0, 0257, 0, 0260, 0,
0261, 0, 0262, 0, 0263, 0, 0264, 0, 0265, 0, 0266, 0, 0267, 0, 0270, 0,
0271, 0, 0272, 0
};
char *letr[26] = {
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
"k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
"u", "v", "w", "x", "y", "z"
};
yylex()
{
int c, ch;
restart:
c = getch();
peekc = -1;
while (c == ' ' || c == '\t')
c = getch();
if (c == '\\') {
(void) getch();
goto restart;
}
if (c <= 'z' && c >= 'a') {
/* look ahead to look for reserved words */
peekc = getch();
if (peekc >= 'a' && peekc <= 'z') {
/* must be reserved word */
if (c == 'i' && peekc == 'f') {
c = _IF;
goto skip;
}
if (c == 'w' && peekc == 'h') {
c = _WHILE;
goto skip;
}
if (c == 'f' && peekc == 'o') {
c = _FOR;
goto skip;
}
if (c == 's' && peekc == 'q') {
c = SQRT;
goto skip;
}
if (c == 'r' && peekc == 'e') {
c = _RETURN;
goto skip;
}
if (c == 'b' && peekc == 'r') {
c = _BREAK;
goto skip;
}
if (c == 'd' && peekc == 'e') {
c = _DEFINE;
goto skip;
}
if (c == 's' && peekc == 'c') {
c = SCALE;
goto skip;
}
if (c == 'b' && peekc == 'a') {
c = BASE;
goto skip;
}
if (c == 'i' && peekc == 'b') {
c = BASE;
goto skip;
}
if (c == 'o' && peekc == 'b') {
c = OBASE;
goto skip;
}
if (c == 'd' && peekc == 'i') {
c = FFF;
goto skip;
}
if (c == 'a' && peekc == 'u') {
c = _AUTO;
goto skip;
}
if (c == 'l' && peekc == 'e') {
c = LENGTH;
goto skip;
}
if (c == 'q' && peekc == 'u') {
getout(0);
}
/* could not be found */
return (error);
skip: /* skip over rest of word */
peekc = -1;
while ((ch = getch()) >= 'a' && ch <= 'z')
;
peekc = ch;
return (c);
}
/* usual case; just one single letter */
yylval.cptr = letr[c-'a'];
return (LETTER);
}
if (c >= '0' && c <= '9' || c >= 'A' && c <= 'F') {
yylval.cc = c;
return (DIGIT);
}
switch (c) {
case '.':
return (DOT);
case '=':
switch ((peekc = getch())) {
case '=':
c = EQ;
goto gotit;
case '+':
c = EQPL;
goto gotit;
case '-':
c = EQMI;
goto gotit;
case '*':
c = EQMUL;
goto gotit;
case '/':
c = EQDIV;
goto gotit;
case '%':
c = EQREM;
goto gotit;
case '^':
c = EQEXP;
goto gotit;
default:
return ('=');
gotit:
peekc = -1;
return (c);
}
case '+':
return (cpeek('+', INCR, '=', EQPL, '+'));
case '-':
return (cpeek('-', DECR, '=', EQMI, '-'));
case '*':
return (cpeek('=', EQMUL, '\0', 0, '*'));
case '%':
return (cpeek('=', EQREM, '\0', 0, '%'));
case '^':
return (cpeek('=', EQEXP, '\0', 0, '^'));
case '<':
return (cpeek('=', LE, '\0', 0, '<'));
case '>':
return (cpeek('=', GE, '\0', 0, '>'));
case '!':
return (cpeek('=', NE, '\0', 0, '!'));
case '/':
if ((peekc = getch()) == '=') {
peekc = -1;
return (EQDIV);
}
if (peekc == '*') {
peekc = -1;
while ((getch() != '*') || ((peekc = getch()) != '/'))
;
peekc = -1;
goto restart;
}
else
return (c);
case '"':
yylval.cptr = str;
while ((c = getch()) != '"') {
*str++ = c;
if (str >= &string[STRING_SIZE-1]) {
yyerror("string space exceeded");
getout(1);
}
}
*str++ = '\0';
return (QSTR);
default:
return (c);
}
}
cpeek(c1, yes1, c2, yes2, none)
char c1, c2, none;
{
int r;
peekc = getch();
if (peekc == c1)
r = yes1;
else if (peekc == c2)
r = yes2;
else
return (none);
peekc = -1;
return (r);
}
getch()
{
int ch;
char mbuf[LINE_MAX];
loop:
ch = (peekc < 0) ? getc(in) : peekc;
peekc = -1;
if (ch != EOF)
return (ch);
if (++ifile >= sargc) {
if (ifile >= sargc+1)
getout(0);
in = stdin;
ln = 0;
goto loop;
}
(void) fclose(in);
if ((in = fopen(sargv[ifile], "r")) != NULL) {
ln = 0;
ss = sargv[ifile];
goto loop;
}
(void) snprintf(mbuf, sizeof (mbuf), "can't open input file %s",
sargv[ifile]);
ln = -1;
ss = "command line";
yyerror(mbuf);
getout(1);
/*NOTREACHED*/
}
#define b_sp_max 5000
int b_space[b_sp_max];
int *b_sp_nxt = { b_space };
int bdebug = 0;
static int *
bundle(int i, ...)
{
va_list ap;
int *q;
va_start(ap, i);
q = b_sp_nxt;
if (bdebug)
printf("bundle %d elements at %o\n", i, q);
while (i-- > 0) {
if (b_sp_nxt >= & b_space[b_sp_max])
yyerror("bundling space exceeded");
*b_sp_nxt++ = va_arg(ap, int);
}
* b_sp_nxt++ = 0;
yyval.iptr = q;
va_end(ap);
return (q);
}
routput(p)
int *p;
{
if (bdebug) printf("routput(%o)\n", p);
if (p >= &b_space[0] && p < &b_space[b_sp_max]) {
/* part of a bundle */
while (*p != 0)
routput(*p++);
}
else
printf((char *)p); /* character string */
}
output(p)
int *p;
{
routput(p);
b_sp_nxt = & b_space[0];
printf("\n");
(void) fflush(stdout);
cp = cary;
crs = rcrs;
}
conout(p, s)
int *p;
char *s;
{
printf("[");
routput(p);
printf("]s%s\n", s);
(void) fflush(stdout);
lev--;
}
yyerror(s)
char *s;
{
if (ifile >= sargc)
ss = "teletype";
if (ss == 0 || *ss == 0)
(void) fprintf(stderr, gettext("%s on line %d\n"), s, ln+1);
else
(void) fprintf(stderr, gettext("%s on line %d, %s\n"),
s, ln+1, ss);
(void) fflush(stderr);
cp = cary;
crs = rcrs;
bindx = 0;
lev = 0;
b_sp_nxt = &b_space[0];
}
checkbuffer()
{
/* Do not exceed the last char in input line buffer */
if (cp >= cpend) {
yyerror("line too long\n");
getout(1);
}
}
pp(s)
int *s;
{
/* puts the relevant stuff on pre and post for the letter s */
(void) bundle(3, "S", s, pre);
pre = yyval.iptr;
(void) bundle(4, post, "L", s, "s.");
post = yyval.iptr;
}
tp(s)
int *s;
{ /* same as pp, but for temps */
bundle(3, "0S", s, pre);
pre = yyval.iptr;
bundle(4, post, "L", s, "s.");
post = yyval.iptr;
}
yyinit(argc, argv)
int argc;
char *argv[];
{
char mbuf[LINE_MAX];
(void) signal(SIGINT, SIG_IGN); /* ignore all interrupts */
sargv = argv;
sargc = argc;
if (sargc == 0)
in = stdin;
else if ((in = fopen(sargv[0], "r")) == NULL) {
(void) snprintf(mbuf, sizeof (mbuf), "can't open input file %s",
sargv[0]);
ln = -1;
ss = "command line";
yyerror(mbuf);
getout(1);
}
ifile = 0;
ln = 0;
ss = sargv[0];
}
static int *
getout(code)
int code;
{
printf("q");
(void) fflush(stdout);
exit(code);
}
int *
getf(p)
char *p;
{
return ((int *) &funtab[2*(*p -0141)]);
}
int *
geta(p)
char *p;
{
return ((int *) &atab[2*(*p - 0141)]);
}
main(argc, argv)
int argc;
char *argv[];
{
int p[2];
int cflag = 0;
int lflag = 0;
int flag = 0;
char **av;
int filecounter = 0;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
#endif
(void) textdomain(TEXT_DOMAIN);
while ((flag = getopt(argc, argv, "dcl")) != EOF) {
switch (flag) {
case 'd':
case 'c':
cflag++;
break;
case 'l':
lflag++;
break;
default:
fflush(stdout);
usage();
break;
}
}
argc -= optind;
av = &argv[optind];
/*
* argc is the count of arguments, which should be filenames,
* remaining in argv. av is a pointer to the first of the
* remaining arguments.
*/
for (filecounter = 0; filecounter < argc; filecounter++) {
if ((strlen(av[filecounter])) >= PATH_MAX) {
(void) fprintf(stderr,
gettext("File argument too long\n"));
exit(2);
}
}
if (lflag) {
/*
* if the user wants to include the math library, prepend
* the math library filename to the argument list by
* overwriting the last option (there must be at least one
* supplied option if this is being done).
*/
av = &argv[optind-1];
av[0] = "/usr/lib/lib.b";
argc++;
}
if (cflag) {
yyinit(argc, av);
yyparse();
exit(0);
}
pipe(p);
if (fork() == 0) {
(void) close(1);
dup(p[1]);
(void) close(p[0]);
(void) close(p[1]);
yyinit(argc, av);
yyparse();
exit(0);
}
(void) close(0);
dup(p[0]);
(void) close(p[0]);
(void) close(p[1]);
#ifdef XPG6
execl("/usr/xpg6/bin/dc", "dc", "-", 0);
#else
execl("/usr/bin/dc", "dc", "-", 0);
#endif
}
static void
usage(void)
{
(void) fprintf(stderr, gettext(
"usage: bc [ -c ] [ -l ] [ file ... ]\n"));
exit(2);
}