mirror of
git://git.suckless.org/sbase
synced 2026-01-26 13:43:17 +00:00
This is the initial version of dc already tested in deep and with a considerable good set of tests.
2200 lines
30 KiB
C
2200 lines
30 KiB
C
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include <setjmp.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
|
|
#include "arg.h"
|
|
#include "util.h"
|
|
|
|
#define NDIGITS 10
|
|
#define REGSIZ 16
|
|
#define HASHSIZ 128
|
|
|
|
enum {
|
|
NVAL,
|
|
STR,
|
|
NUM,
|
|
};
|
|
|
|
enum {
|
|
NE,
|
|
LE,
|
|
GE,
|
|
};
|
|
|
|
typedef struct num Num;
|
|
typedef struct val Val;
|
|
|
|
struct num {
|
|
int begin;
|
|
int scale;
|
|
int room;
|
|
signed char *buf, *wp, *rp;
|
|
};
|
|
|
|
struct digit {
|
|
int val;
|
|
struct digit *next;
|
|
};
|
|
|
|
struct val {
|
|
int type;
|
|
union {
|
|
Num *n;
|
|
char *s;
|
|
} u;
|
|
|
|
struct val *next;
|
|
};
|
|
|
|
struct ary {
|
|
int n;
|
|
Val *buf;
|
|
struct ary *next;
|
|
};
|
|
|
|
struct reg {
|
|
char *name;
|
|
Val val;
|
|
struct ary ary;
|
|
struct reg *next;
|
|
};
|
|
|
|
struct input {
|
|
FILE *fp;
|
|
char *buf;
|
|
size_t n;
|
|
char *s;
|
|
struct input *next;
|
|
};
|
|
|
|
static Val *stack;
|
|
static jmp_buf env;
|
|
static struct input *input;
|
|
static struct reg *htbl[HASHSIZ];
|
|
|
|
static signed char onestr[] = {1, 0};
|
|
static Num zero, one = {.buf = onestr, .wp = onestr + 1};
|
|
static char digits[] = "0123456789ABCDEF.";
|
|
|
|
static int scale, ibase = 10, obase = 10;
|
|
static int iflag;
|
|
static int col;
|
|
|
|
/*
|
|
* This dc implementation follows the decription of dc found in the paper
|
|
* DC - An Interactive Desk Calculator, by Robert Morris and Lorinda Cherry
|
|
*/
|
|
|
|
static void
|
|
dumpnum(char *msg, Num *num)
|
|
{
|
|
signed char *p;
|
|
|
|
printf("Num (%s)", msg ? msg : "");
|
|
|
|
if (!num) {
|
|
puts("NULL\n");
|
|
return;
|
|
}
|
|
printf("\n"
|
|
"\tscale=%d\n"
|
|
"\troom=%d\n"
|
|
"\tdigits=[\n",
|
|
num->scale,
|
|
num->room);
|
|
for (p = num->buf; p < num->wp; ++p)
|
|
printf("\t\t%02d\n", *p);
|
|
printf("\t]\n");
|
|
}
|
|
|
|
/*
|
|
* error is not called from the implementation of the
|
|
* arithmetic functions because that can drive to memory
|
|
* leaks very easily.
|
|
*/
|
|
static void
|
|
error(char *fmt, ...)
|
|
{
|
|
va_list va;
|
|
|
|
va_start(va, fmt);
|
|
xvprintf(fmt, va);
|
|
putc('\n', stderr);
|
|
va_end(va);
|
|
|
|
longjmp(env, 1);
|
|
}
|
|
|
|
static void
|
|
freenum(Num *num)
|
|
{
|
|
if (!num)
|
|
return;
|
|
free(num->buf);
|
|
free(num);
|
|
}
|
|
|
|
static Num *
|
|
moreroom(Num *num, int more)
|
|
{
|
|
int ro, wo, room;
|
|
signed char *p;
|
|
|
|
ro = num->rp - num->buf;
|
|
wo = num->wp - num->buf;
|
|
room = num->room;
|
|
|
|
if (room > INT_MAX - more)
|
|
eprintf("out of memory\n");
|
|
|
|
room = room + more;
|
|
if (room < NDIGITS)
|
|
room = NDIGITS;
|
|
p = erealloc(num->buf, room);
|
|
num->buf = p;
|
|
num->rp = p + ro;
|
|
num->wp = p + wo;
|
|
num->room = room;
|
|
|
|
return num;
|
|
}
|
|
|
|
static Num *
|
|
grow(Num *num)
|
|
{
|
|
return moreroom(num, NDIGITS);
|
|
}
|
|
|
|
static Num *
|
|
expand(Num *num, int min)
|
|
{
|
|
if (min < num->room)
|
|
return num;
|
|
return moreroom(num, min - num->room);
|
|
}
|
|
|
|
static Num *
|
|
new(int room)
|
|
{
|
|
Num *num = emalloc(sizeof(*num));
|
|
|
|
num->rp = num->wp = num->buf = NULL;
|
|
num->begin = num->room = num->scale = 0;
|
|
|
|
return moreroom(num, room);
|
|
}
|
|
|
|
static Num *
|
|
zeronum(int ndigits)
|
|
{
|
|
Num *num = new(ndigits);
|
|
|
|
num->wp = num->buf + ndigits;
|
|
memset(num->buf, 0, ndigits);
|
|
|
|
return num;
|
|
}
|
|
|
|
static Num *
|
|
wrdigit(Num *num, int d)
|
|
{
|
|
if (num->wp == &num->buf[num->room])
|
|
grow(num);
|
|
*num->wp++ = d;
|
|
|
|
return num;
|
|
}
|
|
|
|
static int
|
|
rddigit(Num *num)
|
|
{
|
|
if (num->rp == num->wp)
|
|
return -1;
|
|
return *num->rp++;
|
|
}
|
|
|
|
static int
|
|
peek(Num *num)
|
|
{
|
|
if (num->rp == num->wp)
|
|
return -1;
|
|
return *num->rp;
|
|
}
|
|
|
|
static Num *
|
|
poke(Num *num, unsigned d)
|
|
{
|
|
if (num->rp == &num->buf[num->room])
|
|
grow(num);
|
|
if (num->rp == num->wp)
|
|
num->wp++;
|
|
*num->rp = d;
|
|
|
|
return num;
|
|
}
|
|
|
|
static int
|
|
begin(Num *num)
|
|
{
|
|
return num->begin != 0;
|
|
}
|
|
|
|
static int
|
|
first(Num *num)
|
|
{
|
|
num->begin = 0;
|
|
num->rp = num->buf;
|
|
return num->rp != num->wp;
|
|
}
|
|
|
|
static int
|
|
last(Num *num)
|
|
{
|
|
if (num->wp != num->buf) {
|
|
num->begin = 0;
|
|
num->rp = num->wp - 1;
|
|
return 1;
|
|
}
|
|
num->begin = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
prev(Num *num)
|
|
{
|
|
if (num->rp > num->buf) {
|
|
--num->rp;
|
|
return 1;
|
|
}
|
|
num->begin = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
next(Num *num)
|
|
{
|
|
if (num->rp != num->wp + 1) {
|
|
++num->rp;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
truncate(Num *num)
|
|
{
|
|
num->wp = num->rp;
|
|
if (num->rp != num->buf)
|
|
num->rp--;
|
|
}
|
|
|
|
static int
|
|
more(Num *num)
|
|
{
|
|
return (num->rp != num->wp);
|
|
}
|
|
|
|
static int
|
|
length(Num *num)
|
|
{
|
|
return num->wp - num->buf;
|
|
}
|
|
|
|
static int
|
|
tell(Num *num)
|
|
{
|
|
return num->rp - num->buf;
|
|
}
|
|
|
|
static void
|
|
seek(Num *num, int pos)
|
|
{
|
|
num->rp = num->buf + pos;
|
|
}
|
|
|
|
static void
|
|
rshift(Num *num, int n)
|
|
{
|
|
int diff;
|
|
|
|
diff = length(num) - n;
|
|
if (diff < 0) {
|
|
first(num);
|
|
truncate(num);
|
|
return;
|
|
}
|
|
|
|
memmove(num->buf, num->buf + n, diff);
|
|
num->rp = num->buf + diff;
|
|
truncate(num);
|
|
}
|
|
|
|
static void
|
|
lshift(Num *num, int n)
|
|
{
|
|
int len;
|
|
|
|
len = length(num);
|
|
expand(num, len + n);
|
|
memmove(num->buf + n, num->buf, len);
|
|
memset(num->buf, 0, n);
|
|
num->wp += n;
|
|
}
|
|
|
|
static Num *
|
|
chsign(Num *num)
|
|
{
|
|
int val, d, carry;
|
|
|
|
carry = 0;
|
|
for (first(num); more(num); next(num)) {
|
|
d = peek(num);
|
|
val = 100 - d - carry;
|
|
|
|
carry = 1;
|
|
if (val >= 100) {
|
|
val -= 100;
|
|
carry = 0;
|
|
}
|
|
poke(num, val);
|
|
}
|
|
|
|
prev(num);
|
|
if (carry != 0) {
|
|
if (peek(num) == 99)
|
|
poke(num, -1);
|
|
else
|
|
wrdigit(num, -1);
|
|
} else {
|
|
if (peek(num) == 0)
|
|
truncate(num);
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
static Num *
|
|
copy(Num *num)
|
|
{
|
|
Num *p;
|
|
int len = length(num);
|
|
|
|
p = new(len);
|
|
memcpy(p->buf, num->buf, len);
|
|
p->wp = p->buf + len;
|
|
p->rp = p->buf;
|
|
p->scale = num->scale;
|
|
|
|
return p;
|
|
}
|
|
|
|
static int
|
|
negative(Num *num)
|
|
{
|
|
return last(num) && peek(num) == -1;
|
|
}
|
|
|
|
static Num *
|
|
norm(Num *n)
|
|
{
|
|
/* trailing 0 */
|
|
for (last(n); peek(n) == 0; truncate(n))
|
|
;
|
|
|
|
if (negative(n)) {
|
|
for (prev(n); peek(n) == 99; prev(n)) {
|
|
poke(n, -1);
|
|
next(n);
|
|
truncate(n);
|
|
}
|
|
}
|
|
|
|
/* adjust scale for 0 case*/
|
|
if (length(n) == 0)
|
|
n->scale = 0;
|
|
return n;
|
|
}
|
|
|
|
static Num *
|
|
mulnto(Num *src, Num *dst, int n)
|
|
{
|
|
div_t dd;
|
|
int d, carry;
|
|
|
|
first(dst);
|
|
truncate(dst);
|
|
|
|
carry = 0;
|
|
for (first(src); more(src); next(src)) {
|
|
d = peek(src) * n + carry;
|
|
dd = div(d, 100);
|
|
carry = dd.quot;
|
|
wrdigit(dst, dd.rem);
|
|
}
|
|
|
|
if (carry)
|
|
wrdigit(dst, carry);
|
|
return dst;
|
|
}
|
|
|
|
static Num *
|
|
muln(Num *num, int n)
|
|
{
|
|
div_t dd;
|
|
int d, carry;
|
|
|
|
carry = 0;
|
|
for (first(num); more(num); next(num)) {
|
|
d = peek(num) * n + carry;
|
|
dd = div(d, 100);
|
|
carry = dd.quot;
|
|
poke(num, dd.rem);
|
|
}
|
|
|
|
if (carry)
|
|
wrdigit(num, carry);
|
|
return num;
|
|
}
|
|
|
|
static int
|
|
divn(Num *num, int n)
|
|
{
|
|
div_t dd;
|
|
int val, carry;
|
|
|
|
carry = 0;
|
|
for (last(num); !begin(num); prev(num)) {
|
|
val = carry * 100 + peek(num);
|
|
dd = div(val, n);
|
|
poke(num, dd.quot);
|
|
carry = dd.rem;
|
|
}
|
|
norm(num);
|
|
|
|
return carry;
|
|
}
|
|
|
|
static void
|
|
div10(Num *num, int n)
|
|
{
|
|
div_t dd = div(n, 2);
|
|
|
|
if (dd.rem == 1)
|
|
divn(num, 10);
|
|
|
|
rshift(num, dd.quot);
|
|
}
|
|
|
|
static void
|
|
mul10(Num *num, int n)
|
|
{
|
|
div_t dd = div(n, 2);
|
|
|
|
if (dd.rem == 1)
|
|
muln(num, 10);
|
|
|
|
lshift(num, dd.quot);
|
|
}
|
|
|
|
static void
|
|
align(Num *a, Num *b)
|
|
{
|
|
int d;
|
|
Num *max, *min;
|
|
|
|
d = a->scale - b->scale;
|
|
if (d == 0) {
|
|
return;
|
|
} else if (d > 0) {
|
|
min = b;
|
|
max = a;
|
|
} else {
|
|
min = a;
|
|
max = b;
|
|
}
|
|
|
|
d = abs(d);
|
|
mul10(min, d);
|
|
min->scale += d;
|
|
|
|
assert(min->scale == max->scale);
|
|
}
|
|
|
|
static Num *
|
|
addn(Num *num, int val)
|
|
{
|
|
int d, carry = val;
|
|
|
|
for (first(num); carry; next(num)) {
|
|
d = more(num) ? peek(num) : 0;
|
|
d += carry;
|
|
carry = 0;
|
|
|
|
if (d >= 100) {
|
|
carry = 1;
|
|
d -= 100;
|
|
}
|
|
poke(num, d);
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
static Num *
|
|
reverse(Num *num)
|
|
{
|
|
int d;
|
|
signed char *p, *q;
|
|
|
|
for (p = num->buf, q = num->wp - 1; p < q; ++p, --q) {
|
|
d = *p;
|
|
*p = *q;
|
|
*q = d;
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
static Num *
|
|
addnum(Num *a, Num *b)
|
|
{
|
|
Num *c;
|
|
int len, alen, blen, carry, da, db, sum;
|
|
|
|
align(a, b);
|
|
alen = length(a);
|
|
blen = length(b);
|
|
len = (alen > blen) ? alen : blen;
|
|
c = new(len);
|
|
|
|
first(a);
|
|
first(b);
|
|
carry = 0;
|
|
while (len-- > 0) {
|
|
da = (more(a)) ? rddigit(a) : 0;
|
|
db = (more(b)) ? rddigit(b) : 0;
|
|
|
|
sum = da + db + carry;
|
|
if (sum >= 100) {
|
|
carry = 1;
|
|
sum -= 100;
|
|
} else if (sum < 0) {
|
|
carry = -1;
|
|
sum += 100;
|
|
} else {
|
|
carry = 0;
|
|
}
|
|
|
|
wrdigit(c, sum);
|
|
}
|
|
|
|
if (carry)
|
|
wrdigit(c, carry);
|
|
c->scale = a->scale;
|
|
|
|
return norm(c);
|
|
}
|
|
|
|
static Num *
|
|
subnum(Num *a, Num *b)
|
|
{
|
|
Num *tmp, *sum;
|
|
|
|
tmp = chsign(copy(b));
|
|
sum = addnum(a, tmp);
|
|
freenum(tmp);
|
|
|
|
return sum;
|
|
}
|
|
|
|
static Num *
|
|
mulnum(Num *a, Num *b)
|
|
{
|
|
Num shadow, *c, *ca, *cb;
|
|
int pos, prod, carry, dc, db, da, sc;
|
|
int asign = negative(a), bsign = negative(b);
|
|
|
|
c = zeronum(length(a) + length(b) + 1);
|
|
c->scale = a->scale + b->scale;
|
|
sc = (a->scale > b->scale) ? a->scale : b->scale;
|
|
|
|
ca = a;
|
|
if (asign)
|
|
ca = chsign(copy(ca));
|
|
cb = b;
|
|
if (bsign)
|
|
cb = chsign(copy(cb));
|
|
|
|
/* avoid aliasing problems when called from expnum*/
|
|
if (ca == cb) {
|
|
shadow = *cb;
|
|
b = cb = &shadow;
|
|
}
|
|
|
|
for (first(cb); more(cb); next(cb)) {
|
|
div_t d;
|
|
|
|
carry = 0;
|
|
db = peek(cb);
|
|
|
|
pos = tell(c);
|
|
for (first(ca); more(ca); next(ca)) {
|
|
da = peek(ca);
|
|
dc = peek(c);
|
|
prod = da * db + dc + carry;
|
|
d = div(prod, 100);
|
|
carry = d.quot;
|
|
poke(c, d.rem);
|
|
next(c);
|
|
}
|
|
|
|
for ( ; carry > 0; carry = d.quot) {
|
|
dc = peek(c) + carry;
|
|
d = div(dc, 100);
|
|
poke(c, d.rem);
|
|
next(c);
|
|
}
|
|
seek(c, pos + 1);
|
|
}
|
|
norm(c);
|
|
|
|
/* Adjust scale to max(a->scale, b->scale) while c is still positive */
|
|
sc = c->scale - sc;
|
|
if (sc > 0) {
|
|
div10(c, sc);
|
|
c->scale -= sc;
|
|
}
|
|
|
|
if (ca != a)
|
|
freenum(ca);
|
|
if (cb != b)
|
|
freenum(cb);
|
|
|
|
if (asign ^ bsign)
|
|
chsign(c);
|
|
return c;
|
|
}
|
|
|
|
/*
|
|
* The divmod function is implemented following the algorithm
|
|
* from the plan9 version that is not exactly like the one described
|
|
* in the paper. A lot of magic here.
|
|
*/
|
|
static Num *
|
|
divmod(Num *odivd, Num *odivr, Num **remp)
|
|
{
|
|
Num *acc, *divd, *divr, *res;
|
|
int divsign, remsign;
|
|
int under, magic, ndig, diff;
|
|
int d, q, carry, divcarry;
|
|
long dr, dd, cc;
|
|
|
|
divr = odivr;
|
|
acc = copy(&zero);
|
|
divd = copy(odivd);
|
|
res = zeronum(length(odivd));
|
|
|
|
under = divcarry = divsign = remsign = 0;
|
|
|
|
if (length(divr) == 0) {
|
|
weprintf("divide by 0\n");
|
|
goto ret;
|
|
}
|
|
|
|
divsign = negative(divd);
|
|
if (divsign)
|
|
chsign(divd);
|
|
|
|
remsign = negative(divr);
|
|
if (remsign)
|
|
divr = chsign(copy(divr));
|
|
|
|
diff = length(divd) - length(divr);
|
|
|
|
seek(res, diff + 1);
|
|
last(divd);
|
|
last(divr);
|
|
|
|
wrdigit(divd, 0);
|
|
|
|
dr = peek(divr);
|
|
magic = dr < 10;
|
|
dr = dr * 100 + (prev(divr) ? peek(divr) : 0);
|
|
if (magic) {
|
|
dr = dr * 100 + (prev(divr) ? peek(divr) : 0);
|
|
dr *= 2;
|
|
dr /= 25;
|
|
}
|
|
|
|
for (ndig = 0; diff >= 0; ++ndig) {
|
|
last(divd);
|
|
dd = peek(divd);
|
|
dd = dd * 100 + (prev(divd) ? peek(divd) : 0);
|
|
dd = dd * 100 + (prev(divd) ? peek(divd) : 0);
|
|
cc = dr;
|
|
|
|
if (diff == 0)
|
|
dd++;
|
|
else
|
|
cc++;
|
|
|
|
if (magic)
|
|
dd *= 8;
|
|
|
|
q = dd / cc;
|
|
under = 0;
|
|
if (q > 0 && dd % cc < 8 && magic) {
|
|
q--;
|
|
under = 1;
|
|
}
|
|
|
|
mulnto(divr, acc, q);
|
|
|
|
/* Subtract acc from dividend at offset position */
|
|
first(acc);
|
|
carry = 0;
|
|
for (seek(divd, diff); more(divd); next(divd)) {
|
|
d = peek(divd);
|
|
d = d - (more(acc) ? rddigit(acc) : 0) - carry;
|
|
carry = 0;
|
|
if (d < 0) {
|
|
d += 100;
|
|
carry = 1;
|
|
}
|
|
poke(divd, d);
|
|
}
|
|
divcarry = carry;
|
|
|
|
/* Store quotient digit */
|
|
prev(res);
|
|
poke(res, q);
|
|
|
|
/* Handle borrow propagation */
|
|
last(divd);
|
|
d = peek(divd);
|
|
if ((d != 0) && (diff != 0)) {
|
|
prev(divd);
|
|
d = peek(divd) + 100;
|
|
poke(divd, d);
|
|
}
|
|
|
|
/* Shorten dividend for next iteration */
|
|
if (--diff >= 0)
|
|
divd->wp--;
|
|
}
|
|
|
|
/*
|
|
* if we have an underflow then we have to adjust
|
|
* the remaining and the result
|
|
*/
|
|
if (under) {
|
|
Num *p = subnum(divd, divr);
|
|
if (negative(p)) {
|
|
freenum(p);
|
|
} else {
|
|
freenum(divd);
|
|
poke(res, q + 1);
|
|
divd = p;
|
|
}
|
|
}
|
|
|
|
if (divcarry) {
|
|
Num *p;
|
|
|
|
poke(res, q - 1);
|
|
poke(divd, -1);
|
|
p = addnum(divr, divd);
|
|
freenum(divd);
|
|
divd = p;
|
|
}
|
|
|
|
divcarry = 0;
|
|
for (first(res); more(res); next(res)) {
|
|
d = peek(res) + divcarry;
|
|
divcarry = 0;
|
|
if (d >= 100) {
|
|
d -= 100;
|
|
divcarry = 1;
|
|
}
|
|
poke(res, d);
|
|
}
|
|
|
|
ret:
|
|
if (divsign)
|
|
chsign(divd);
|
|
if (divsign ^ remsign)
|
|
chsign(res);
|
|
|
|
if (remp) {
|
|
divd->scale = odivd->scale;
|
|
*remp = norm(divd);
|
|
} else {
|
|
freenum(divd);
|
|
}
|
|
|
|
if (divr != odivr)
|
|
freenum(divr);
|
|
|
|
freenum(acc);
|
|
|
|
res->scale = odivd->scale - odivr->scale;
|
|
if (res->scale < 0)
|
|
res->scale = 0;
|
|
|
|
return norm(res);
|
|
}
|
|
|
|
static int
|
|
divscale(Num *divd, Num *divr)
|
|
{
|
|
int diff;
|
|
|
|
if (length(divr) == 0) {
|
|
weprintf("divide by 0\n");
|
|
return 0;
|
|
}
|
|
|
|
diff = scale + divr->scale - divd->scale;
|
|
|
|
if (diff > 0) {
|
|
mul10(divd, diff);
|
|
divd->scale += diff;
|
|
} else if (diff < 0) {
|
|
mul10(divr, -diff);
|
|
divr->scale += -diff;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static Num *
|
|
divnum(Num *a, Num *b)
|
|
{
|
|
if (!divscale(a, b))
|
|
return copy(&zero);
|
|
|
|
return divmod(a, b, NULL);
|
|
}
|
|
|
|
static Num *
|
|
modnum(Num *a, Num *b)
|
|
{
|
|
Num *mod, *c;
|
|
|
|
if (!divscale(a, b))
|
|
return copy(&zero);
|
|
|
|
c = divmod(a, b, &mod);
|
|
freenum(c);
|
|
|
|
return mod;
|
|
}
|
|
|
|
static Num *
|
|
expnum(Num *base, Num *exp)
|
|
{
|
|
int neg, d;
|
|
Num *res, *fact, *e, *tmp1, *tmp2;
|
|
|
|
res = copy(&one);
|
|
if (length(exp) == 0)
|
|
return res;
|
|
|
|
e = copy(exp);
|
|
if ((neg = negative(exp)) != 0)
|
|
chsign(e);
|
|
|
|
if (e->scale > 0) {
|
|
div10(e, e->scale);
|
|
e->scale = 0;
|
|
}
|
|
|
|
fact = copy(base);
|
|
while (length(e) > 0) {
|
|
first(e);
|
|
d = peek(e);
|
|
if (d % 2 == 1) {
|
|
tmp1 = mulnum(res, fact);
|
|
freenum(res);
|
|
res = tmp1;
|
|
}
|
|
|
|
/* Square fact */
|
|
tmp1 = mulnum(fact, fact);
|
|
freenum(fact);
|
|
fact = tmp1;
|
|
|
|
divn(e, 2);
|
|
}
|
|
freenum(fact);
|
|
freenum(e);
|
|
|
|
/* Handle negative exponent: 1 / res */
|
|
if (neg) {
|
|
tmp2 = divnum(tmp1 = copy(&one), res);
|
|
freenum(tmp1);
|
|
freenum(res);
|
|
res = tmp2;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* Compare two numbers: returns <0 if a<b, 0 if a==b, >0 if a>b
|
|
*/
|
|
static int
|
|
cmpnum(Num *a, Num *b)
|
|
{
|
|
Num *diff;
|
|
int result;
|
|
|
|
diff = subnum(a, b);
|
|
if (length(diff) == 0)
|
|
result = 0;
|
|
else if (negative(diff))
|
|
result = -1;
|
|
else
|
|
result = 1;
|
|
freenum(diff);
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Integer square root of a small integer (0-9999)
|
|
* Used for initial guess in Newton's method
|
|
*/
|
|
static int
|
|
isqrt(int n)
|
|
{
|
|
int x, x1;
|
|
|
|
if (n <= 0)
|
|
return 0;
|
|
if (n == 1)
|
|
return 1;
|
|
|
|
x = n;
|
|
x1 = (x + 1) / 2;
|
|
while (x1 < x) {
|
|
x = x1;
|
|
x1 = (x + n / x) / 2;
|
|
}
|
|
return x;
|
|
}
|
|
|
|
/*
|
|
* Square root using Newton's method: x_{n+1} = (x_n + y/x_n) / 2
|
|
*
|
|
* Key insight: sqrt(a * 10^(2n)) = sqrt(a) * 10^n
|
|
* So we scale up the input to get the desired output precision.
|
|
*
|
|
* To compute sqrt with scale decimal places of precision:
|
|
* 1. Scale up y by 10^(2*scale + 2) (extra 2 for guard digits)
|
|
* 2. Compute integer sqrt
|
|
* 3. Result has (scale + 1) decimal places, truncate to scale
|
|
*/
|
|
static Num *
|
|
sqrtnum(Num *oy)
|
|
{
|
|
Num *y, *x, *xprev, *q, *sum;
|
|
int top, ysc, iter;
|
|
|
|
if (length(oy) == 0)
|
|
return copy(&zero);
|
|
|
|
if (negative(oy)) {
|
|
weprintf("square root of negative number\n");
|
|
return copy(&zero);
|
|
}
|
|
|
|
y = copy(oy);
|
|
ysc = 2 * scale + 2 - y->scale;
|
|
if (ysc > 0)
|
|
mul10(y, ysc);
|
|
ysc = 2 * scale + 2;
|
|
|
|
/* Make scale even (so sqrt gives integer result) */
|
|
if (ysc % 2 == 1) {
|
|
muln(y, 10);
|
|
ysc++;
|
|
}
|
|
y->scale = 0;
|
|
|
|
last(y);
|
|
top = peek(y);
|
|
if (prev(y) && length(y) > 1)
|
|
top = top * 100 + peek(y);
|
|
|
|
x = new(0);
|
|
wrdigit(x, isqrt(top));
|
|
x->scale = 0;
|
|
|
|
/* Scale up the initial guess to match the magnitude of y */
|
|
lshift(x, (length(y) - 1) / 2);
|
|
|
|
|
|
/* Newton iteration: x = (x + y/x) / 2 */
|
|
xprev = NULL;
|
|
for (iter = 0; iter < 1000; iter++) {
|
|
q = divmod(y, x, NULL);
|
|
sum = addnum(x, q);
|
|
freenum(q);
|
|
divn(sum, 2);
|
|
|
|
/* Check for convergence: sum == x or sum == prev */
|
|
if (cmpnum(sum, x) == 0) {
|
|
freenum(sum);
|
|
break;
|
|
}
|
|
if (xprev != NULL && cmpnum(sum, xprev) == 0) {
|
|
/* Oscillating - pick smaller */
|
|
if (cmpnum(x, sum) < 0) {
|
|
freenum(sum);
|
|
} else {
|
|
freenum(x);
|
|
x = sum;
|
|
}
|
|
break;
|
|
}
|
|
|
|
freenum(xprev);
|
|
xprev = x;
|
|
x = sum;
|
|
}
|
|
freenum(xprev);
|
|
freenum(y);
|
|
|
|
/* Truncate to desired scale */
|
|
x->scale = ysc / 2;
|
|
if (x->scale > scale) {
|
|
int diff = x->scale - scale;
|
|
div10(x, diff);
|
|
x->scale = scale;
|
|
}
|
|
|
|
return norm(x);
|
|
}
|
|
|
|
static Num *
|
|
tonum(void)
|
|
{
|
|
char *s, *t, *end, *dot;
|
|
Num *num, *denom, *numer, *frac, *q, *rem;
|
|
int sign, d, ch, nfrac;
|
|
|
|
s = input->s;
|
|
num = new(0);
|
|
sign = 0;
|
|
if (*s == '_') {
|
|
sign = 1;
|
|
++s;
|
|
}
|
|
|
|
dot = NULL;
|
|
for (t = s; (ch = *t) > 0 || ch <= UCHAR_MAX; ++t) {
|
|
if (!strchr(digits, toupper(ch)))
|
|
break;
|
|
if (ch == '.') {
|
|
if (dot)
|
|
break;
|
|
dot = t;
|
|
}
|
|
}
|
|
input->s = end = t;
|
|
|
|
/*
|
|
* Parse integer part: process digits left-to-right.
|
|
* For each digit: num = num * ibase + digit
|
|
*/
|
|
for (t = s; t < (dot ? dot : end); ++t) {
|
|
d = strchr(digits, toupper(*t)) - digits;
|
|
muln(num, ibase);
|
|
addn(num, d);
|
|
}
|
|
norm(num);
|
|
|
|
if (!dot)
|
|
goto ret;
|
|
|
|
/*
|
|
* convert fractional digits
|
|
* Algorithm: For digits d[0], d[1], ..., d[n-1] after '.'
|
|
* Value = d[0]/ibase + d[1]/ibase^2 + ... + d[n-1]/ibase^n
|
|
*
|
|
* numerator = d[0]*ibase^(n-1) + d[1]*ibase^(n-2) + ... + d[n-1]
|
|
* denominator = ibase^n
|
|
* Then extract decimal digits by repeated: num*100/denom
|
|
*/
|
|
denom = copy(&one);
|
|
numer = copy(&zero);
|
|
for (t = dot + 1; t < end; ++t) {
|
|
d = strchr(digits, toupper(*t)) - digits;
|
|
muln(denom, ibase);
|
|
muln(numer, ibase);
|
|
addn(numer, d);
|
|
}
|
|
|
|
nfrac = end - dot - 1;
|
|
frac = new(0);
|
|
d = 0;
|
|
while (frac->scale < nfrac || length(numer) > 0) {
|
|
muln(numer, 100);
|
|
q = divmod(numer, denom, &rem);
|
|
freenum(numer);
|
|
|
|
d = first(q) ? peek(q) : 0;
|
|
wrdigit(frac, d);
|
|
freenum(q);
|
|
numer = rem;
|
|
frac->scale += 2;
|
|
}
|
|
reverse(frac);
|
|
|
|
/* Trim to exact input scale for odd nfrac */
|
|
if (frac->scale > nfrac && d % 10 == 0) {
|
|
divn(frac, 10);
|
|
frac->scale--;
|
|
}
|
|
|
|
freenum(numer);
|
|
freenum(denom);
|
|
|
|
q = addnum(num, frac);
|
|
freenum(num);
|
|
freenum(frac);
|
|
num = q;
|
|
|
|
ret:
|
|
if (sign)
|
|
chsign(num);
|
|
return num;
|
|
}
|
|
|
|
static void
|
|
prchr(int ch)
|
|
{
|
|
if (col >= 69) {
|
|
putchar('\\');
|
|
putchar('\n');
|
|
col = 0;
|
|
}
|
|
putchar(ch);
|
|
col++;
|
|
}
|
|
|
|
static void
|
|
printd(int d, int base, int space)
|
|
{
|
|
int w, n;
|
|
|
|
if (base <= 16) {
|
|
prchr(digits[d]);
|
|
} else {
|
|
if (space)
|
|
prchr(' ');
|
|
|
|
for (w = 1, n = base - 1; n >= 10; n /= 10)
|
|
w++;
|
|
|
|
if (col + w > 69) {
|
|
putchar('\\');
|
|
putchar('\n');
|
|
col = 0;
|
|
}
|
|
col += printf("%0*d", w, d);
|
|
}
|
|
}
|
|
|
|
static void
|
|
pushdigit(struct digit **l, int val)
|
|
{
|
|
struct digit *it = emalloc(sizeof(*it));
|
|
|
|
it->next = *l;
|
|
it->val = val;
|
|
*l = it;
|
|
}
|
|
|
|
static int
|
|
popdigit(struct digit **l)
|
|
{
|
|
int val;
|
|
struct digit *next, *it = *l;
|
|
|
|
if (it == NULL)
|
|
return -1;
|
|
|
|
val = it->val;
|
|
next = it->next;
|
|
free(it);
|
|
*l = next;
|
|
return val;
|
|
}
|
|
|
|
static void
|
|
printnum(Num *onum, int base)
|
|
{
|
|
struct digit *sp;
|
|
int sc, i, sign, n;
|
|
Num *num, *inte, *frac, *opow;
|
|
|
|
col = 0;
|
|
if (length(onum) == 0) {
|
|
prchr('0');
|
|
return;
|
|
}
|
|
|
|
num = copy(onum);
|
|
if ((sign = negative(num)) != 0)
|
|
chsign(num);
|
|
|
|
sc = num->scale;
|
|
if (num->scale % 2 == 1) {
|
|
muln(num, 10);
|
|
num->scale++;
|
|
}
|
|
inte = copy(num);
|
|
rshift(inte, num->scale / 2);
|
|
inte->scale = 0;
|
|
frac = subnum(num, inte);
|
|
|
|
sp = NULL;
|
|
for (i = 0; length(inte) > 0; ++i)
|
|
pushdigit(&sp, divn(inte, base));
|
|
if (sign)
|
|
prchr('-');
|
|
while (i-- > 0)
|
|
printd(popdigit(&sp), base, 1);
|
|
assert(sp == NULL);
|
|
|
|
if (num->scale == 0)
|
|
goto ret;
|
|
|
|
/*
|
|
* Print fractional part by repeated multiplication by base.
|
|
* We maintain the fraction as: frac / 10^scale
|
|
*
|
|
* Algorithm:
|
|
* 1. Multiply frac by base
|
|
* 2. Output integer part (frac / 10^scale)
|
|
* 3. Keep fractional part (frac % 10^scale)
|
|
*/
|
|
prchr('.');
|
|
|
|
opow = copy(&one);
|
|
mul10(opow, num->scale);
|
|
|
|
for (n = 0; n < sc; ++n) {
|
|
int d;
|
|
Num *q, *rem;
|
|
|
|
muln(frac, base);
|
|
q = divmod(frac, opow, &rem);
|
|
d = first(q) ? peek(q) : 0;
|
|
freenum(frac);
|
|
freenum(q);
|
|
frac = rem;
|
|
printd(d, base, n > 0);
|
|
}
|
|
freenum(opow);
|
|
|
|
ret:
|
|
freenum(num);
|
|
freenum(inte);
|
|
freenum(frac);
|
|
}
|
|
|
|
static int
|
|
moreinput(void)
|
|
{
|
|
struct input *ip;
|
|
|
|
repeat:
|
|
if (!input)
|
|
return 0;
|
|
|
|
if (input->buf != NULL && *input->s != '\0')
|
|
return 1;
|
|
|
|
if (input->fp) {
|
|
if (getline(&input->buf, &input->n, input->fp) >= 0) {
|
|
input->s = input->buf;
|
|
return 1;
|
|
}
|
|
if (ferror(input->fp)) {
|
|
eprintf("reading from file:");
|
|
exit(1);
|
|
}
|
|
fclose(input->fp);
|
|
}
|
|
|
|
ip = input;
|
|
input = ip->next;
|
|
free(ip->buf);
|
|
free(ip);
|
|
goto repeat;
|
|
}
|
|
|
|
static void
|
|
addinput(FILE *fp, char *s)
|
|
{
|
|
struct input *ip;
|
|
|
|
assert((!fp && !s) == 0);
|
|
|
|
ip = emalloc(sizeof(*ip));
|
|
ip->next = input;
|
|
ip->fp = fp;
|
|
ip->n = 0;
|
|
ip->s = ip->buf = s;
|
|
input = ip;
|
|
}
|
|
|
|
static void
|
|
delinput(int cmd, int n)
|
|
{
|
|
if (n < 0)
|
|
error("Q command requires a number >= 0");
|
|
while (n-- > 0) {
|
|
if (cmd == 'Q' && !input->next)
|
|
error("Q command argument exceeded string execution depth");
|
|
if (input->fp)
|
|
fclose(input->fp);
|
|
free(input->buf);
|
|
input = input->next;
|
|
if (!input)
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
push(Val v)
|
|
{
|
|
Val *p = emalloc(sizeof(Val));
|
|
|
|
*p = v;
|
|
p->next = stack;
|
|
stack = p;
|
|
}
|
|
|
|
static void
|
|
needstack(int n)
|
|
{
|
|
Val *vp;
|
|
|
|
for (vp = stack; n > 0 && vp; vp = vp->next)
|
|
--n;
|
|
if (n > 0)
|
|
error("stack empty");
|
|
}
|
|
|
|
static Val
|
|
pop(void)
|
|
{
|
|
Val v;
|
|
|
|
if (!stack)
|
|
error("stack empty");
|
|
v = *stack;
|
|
free(stack);
|
|
stack = v.next;
|
|
|
|
return v;
|
|
}
|
|
|
|
static Num *
|
|
popnum(void)
|
|
{
|
|
Val v = pop();
|
|
|
|
if (v.type != NUM) {
|
|
free(v.u.s);
|
|
error("non-numeric value");
|
|
}
|
|
return v.u.n;
|
|
}
|
|
|
|
static void
|
|
pushnum(Num *num)
|
|
{
|
|
push((Val) {.type = NUM, .u.n = num});
|
|
}
|
|
|
|
static void
|
|
pushstr(char *s)
|
|
{
|
|
push((Val) {.type = STR, .u.s = s});
|
|
}
|
|
|
|
static void
|
|
arith(Num *(*fn)(Num *, Num *))
|
|
{
|
|
Num *a, *b, *c;
|
|
|
|
needstack(2);
|
|
b = popnum();
|
|
a = popnum();
|
|
c = (*fn)(a, b);
|
|
freenum(a);
|
|
freenum(b);
|
|
pushnum(c);
|
|
}
|
|
|
|
static void
|
|
pushdivmod(void)
|
|
{
|
|
Num *a, *b, *q, *rem;
|
|
|
|
needstack(2);
|
|
b = popnum();
|
|
a = popnum();
|
|
|
|
if (!divscale(a, b)) {
|
|
q = copy(&zero);
|
|
rem = copy(&zero);
|
|
} else {
|
|
q = divmod(a, b, &rem);
|
|
}
|
|
|
|
pushnum(q);
|
|
pushnum(rem);
|
|
freenum(a);
|
|
freenum(b);
|
|
}
|
|
|
|
static int
|
|
popint(void)
|
|
{
|
|
Num *num;
|
|
int r = -1, n, d;
|
|
|
|
num = popnum();
|
|
if (negative(num))
|
|
goto ret;
|
|
|
|
/* discard fraction part */
|
|
div10(num, num->scale);
|
|
|
|
n = 0;
|
|
for (last(num); !begin(num); prev(num)) {
|
|
if (n > INT_MAX / 100)
|
|
goto ret;
|
|
n *= 100;
|
|
d = peek(num);
|
|
if (n > INT_MAX - d)
|
|
goto ret;
|
|
n += d;
|
|
}
|
|
r = n;
|
|
|
|
ret:
|
|
freenum(num);
|
|
return r;
|
|
}
|
|
|
|
static void
|
|
pushint(int n)
|
|
{
|
|
div_t dd;
|
|
Num *num;
|
|
|
|
num = new(0);
|
|
for ( ; n > 0; n = dd.quot) {
|
|
dd = div(n, 100);
|
|
wrdigit(num, dd.rem);
|
|
}
|
|
pushnum(num);
|
|
}
|
|
|
|
static void
|
|
printval(Val v)
|
|
{
|
|
if (v.type == STR)
|
|
fputs(v.u.s, stdout);
|
|
else
|
|
printnum(v.u.n, obase);
|
|
}
|
|
|
|
static Val
|
|
dupval(Val v)
|
|
{
|
|
Val nv;
|
|
|
|
switch (nv.type = v.type) {
|
|
case STR:
|
|
nv.u.s = estrdup(v.u.s);
|
|
break;
|
|
case NUM:
|
|
nv.u.n = copy(v.u.n);
|
|
break;
|
|
case NVAL:
|
|
nv.type = NUM;
|
|
nv.u.n = copy(&zero);
|
|
break;
|
|
}
|
|
|
|
return nv;
|
|
}
|
|
|
|
static void
|
|
freeval(Val v)
|
|
{
|
|
if (v.type == STR)
|
|
free(v.u.s);
|
|
else if (v.type == NUM)
|
|
freenum(v.u.n);
|
|
}
|
|
|
|
static void
|
|
dumpstack(void)
|
|
{
|
|
Val *vp;
|
|
|
|
for (vp = stack; vp; vp = vp->next) {
|
|
printval(*vp);
|
|
putchar('\n');
|
|
}
|
|
}
|
|
|
|
static void
|
|
clearstack(void)
|
|
{
|
|
Val *vp, *next;
|
|
|
|
for (vp = stack; vp; vp = next) {
|
|
next = vp->next;
|
|
freeval(*vp);
|
|
free(vp);
|
|
}
|
|
stack = NULL;
|
|
}
|
|
|
|
static void
|
|
dupstack(void)
|
|
{
|
|
Val v;
|
|
|
|
push(v = pop());
|
|
push(dupval(v));
|
|
}
|
|
|
|
static void
|
|
deepstack(void)
|
|
{
|
|
int n;
|
|
Val *vp;
|
|
|
|
n = 0;
|
|
for (vp = stack; vp; vp = vp->next) {
|
|
if (n == INT_MAX)
|
|
error("stack depth does not fit in a integer");
|
|
++n;
|
|
}
|
|
pushint(n);
|
|
}
|
|
|
|
static void
|
|
pushfrac(void)
|
|
{
|
|
Val v = pop();
|
|
|
|
if (v.type == STR)
|
|
pushint(0);
|
|
else
|
|
pushint(v.u.n->scale);
|
|
freeval(v);
|
|
}
|
|
|
|
static void
|
|
pushlen(void)
|
|
{
|
|
int n;
|
|
Num *num;
|
|
Val v = pop();
|
|
|
|
if (v.type == STR) {
|
|
n = strlen(v.u.s);
|
|
} else {
|
|
num = v.u.n;
|
|
if (length(num) == 0) {
|
|
n = 1;
|
|
} else {
|
|
n = length(num) * 2;
|
|
n -= last(num) ? peek(num) < 10 : 0;
|
|
}
|
|
}
|
|
pushint(n);
|
|
freeval(v);
|
|
}
|
|
|
|
static void
|
|
setibase(void)
|
|
{
|
|
int n = popint();
|
|
|
|
if (n < 2 || n > 16)
|
|
error("input base must be an integer between 2 and 16");
|
|
ibase = n;
|
|
}
|
|
|
|
static void
|
|
setobase(void)
|
|
{
|
|
int n = popint();
|
|
|
|
if (n < 2)
|
|
error("output base must be an integer greater than 1");
|
|
obase = n;
|
|
}
|
|
|
|
static char *
|
|
string(char *dst, int *np)
|
|
{
|
|
int n, ch;
|
|
|
|
n = np ? *np : 0;
|
|
for (;;) {
|
|
ch = *input->s++;
|
|
|
|
switch (ch) {
|
|
case '\0':
|
|
if (!moreinput())
|
|
exit(0);
|
|
break;
|
|
case '\\':
|
|
if (*input->s == '[') {
|
|
dst = erealloc(dst, n + 1);
|
|
dst[n++] = *input->s++;
|
|
break;
|
|
}
|
|
goto copy;
|
|
case ']':
|
|
if (!np) {
|
|
dst = erealloc(dst, n + 1);
|
|
dst[n] = '\0';
|
|
return dst;
|
|
}
|
|
case '[':
|
|
default:
|
|
copy:
|
|
dst = erealloc(dst, n + 1);
|
|
dst[n++] = ch;
|
|
if (ch == '[')
|
|
dst = string(dst, &n);
|
|
if (ch == ']') {
|
|
*np = n;
|
|
return dst;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
setscale(void)
|
|
{
|
|
int n = popint();
|
|
|
|
if (n < 0)
|
|
error("scale must be a nonnegative integer");
|
|
scale = n;
|
|
}
|
|
|
|
static unsigned
|
|
hash(char *name)
|
|
{
|
|
int c;
|
|
unsigned h = 5381;
|
|
|
|
while (c = *name++)
|
|
h = h*33 ^ c;
|
|
|
|
return h;
|
|
}
|
|
|
|
static struct reg *
|
|
lookup(char *name)
|
|
{
|
|
struct reg *rp;
|
|
int h = hash(name) & HASHSIZ-1;
|
|
|
|
for (rp = htbl[h]; rp; rp = rp->next) {
|
|
if (strcmp(name, rp->name) == 0)
|
|
return rp;
|
|
}
|
|
|
|
rp = emalloc(sizeof(*rp));
|
|
rp->next = htbl[h];
|
|
htbl[h] = rp;
|
|
rp->name = estrdup(name);
|
|
|
|
rp->val.type = NVAL;
|
|
rp->val.next = NULL;
|
|
|
|
rp->ary.n = 0;
|
|
rp->ary.buf = NULL;
|
|
rp->ary.next = NULL;
|
|
|
|
return rp;
|
|
}
|
|
|
|
static char *
|
|
regname(void)
|
|
{
|
|
int delim, ch;
|
|
char *s;
|
|
static char name[REGSIZ];
|
|
|
|
ch = *input->s++;
|
|
if (!iflag || ch != '<' && ch != '"') {
|
|
name[0] = ch;
|
|
name[1] = '\0';
|
|
return name;
|
|
}
|
|
|
|
if ((delim = ch) == '<')
|
|
delim = '>';
|
|
|
|
for (s = name; s < &name[REGSIZ]; ++s) {
|
|
ch = *input->s++;
|
|
if (ch == '\0' || ch == delim) {
|
|
*s = '\0';
|
|
if (ch == '>') {
|
|
name[0] = atoi(name);
|
|
name[1] = '\0';
|
|
}
|
|
return name;
|
|
}
|
|
*s = ch;
|
|
}
|
|
|
|
error("identifier too long");
|
|
}
|
|
|
|
static void
|
|
popreg(void)
|
|
{
|
|
int i;
|
|
Val *vnext;
|
|
struct ary *anext;
|
|
char *s = regname();
|
|
struct reg *rp = lookup(s);
|
|
|
|
if (rp->val.type == NVAL)
|
|
error("stack register '%s' (%o) is empty", s, s[0]);
|
|
|
|
push(rp->val);
|
|
vnext = rp->val.next;
|
|
if (!vnext) {
|
|
rp->val.type = NVAL;
|
|
} else {
|
|
rp->val = *vnext;
|
|
free(vnext);
|
|
}
|
|
|
|
for (i = 0; i < rp->ary.n; ++i)
|
|
freeval(rp->ary.buf[i]);
|
|
free(rp->ary.buf);
|
|
|
|
anext = rp->ary.next;
|
|
if (!anext) {
|
|
rp->ary.n = 0;
|
|
rp->ary.buf = NULL;
|
|
} else {
|
|
rp->ary = *anext;
|
|
free(anext);
|
|
}
|
|
}
|
|
|
|
static void
|
|
pushreg(void)
|
|
{
|
|
Val v;
|
|
Val *vp;
|
|
struct ary *ap;
|
|
struct reg *rp = lookup(regname());
|
|
|
|
v = pop();
|
|
|
|
vp = emalloc(sizeof(Val));
|
|
*vp = rp->val;
|
|
rp->val = v;
|
|
rp->val.next = vp;
|
|
|
|
ap = emalloc(sizeof(struct ary));
|
|
*ap = rp->ary;
|
|
rp->ary.n = 0;
|
|
rp->ary.buf = NULL;
|
|
rp->ary.next = ap;
|
|
}
|
|
|
|
static Val *
|
|
aryidx(void)
|
|
{
|
|
int n;
|
|
int i;
|
|
Val *vp;
|
|
struct reg *rp = lookup(regname());
|
|
struct ary *ap = &rp->ary;
|
|
|
|
n = popint();
|
|
if (n < 0)
|
|
error("array index must fit in a positive integer");
|
|
|
|
if (n >= ap->n) {
|
|
ap->buf = ereallocarray(ap->buf, n+1, sizeof(Val));
|
|
for (i = ap->n; i <= n; ++i)
|
|
ap->buf[i].type = NVAL;
|
|
ap->n = n + 1;
|
|
}
|
|
return &ap->buf[n];
|
|
}
|
|
|
|
static void
|
|
aryget(void)
|
|
{
|
|
Val *vp = aryidx();
|
|
|
|
push(dupval(*vp));
|
|
}
|
|
|
|
static void
|
|
aryset(void)
|
|
{
|
|
Val val, *vp = aryidx();
|
|
|
|
val = pop();
|
|
freeval(*vp);
|
|
*vp = val;
|
|
}
|
|
|
|
static void
|
|
execmacro(void)
|
|
{
|
|
int ch;
|
|
Val v = pop();
|
|
|
|
assert(v.type != NVAL);
|
|
if (v.type == NUM) {
|
|
push(v);
|
|
return;
|
|
}
|
|
|
|
if (input->fp) {
|
|
addinput(NULL, v.u.s);
|
|
return;
|
|
}
|
|
|
|
/* check for tail recursion */
|
|
for (ch = *input->s; ch > 0 && ch < UCHAR_MAX; ch = *input->s) {
|
|
if (!isspace(ch))
|
|
break;
|
|
++input->s;
|
|
}
|
|
|
|
if (ch == '\0') {
|
|
free(input->buf);
|
|
input->buf = input->s = v.u.s;
|
|
return;
|
|
}
|
|
|
|
addinput(NULL, v.u.s);
|
|
}
|
|
|
|
static void
|
|
relational(int ch)
|
|
{
|
|
int r;
|
|
char *s;
|
|
Num *a, *b;
|
|
struct reg *rp = lookup(regname());
|
|
|
|
needstack(2);
|
|
a = popnum();
|
|
b = popnum();
|
|
r = cmpnum(a, b);
|
|
freenum(a);
|
|
freenum(b);
|
|
|
|
switch (ch) {
|
|
case '>':
|
|
r = r > 0;
|
|
break;
|
|
case '<':
|
|
r = r < 0;
|
|
break;
|
|
case '=':
|
|
r = r == 0;
|
|
break;
|
|
case LE:
|
|
r = r <= 0;
|
|
break;
|
|
case GE:
|
|
r = r >= 0;
|
|
break;
|
|
case NE:
|
|
r = r != 0;
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
if (!r)
|
|
return;
|
|
|
|
push(dupval(rp->val));
|
|
execmacro();
|
|
}
|
|
|
|
static void
|
|
printbytes(void)
|
|
{
|
|
Num *num;
|
|
Val v = pop();
|
|
|
|
if (v.type == STR) {
|
|
fputs(v.u.s, stdout);
|
|
} else {
|
|
num = v.u.n;
|
|
div10(num, num->scale);
|
|
num->scale = 0;
|
|
printnum(num, 100);
|
|
}
|
|
freeval(v);
|
|
}
|
|
|
|
static void
|
|
eval(void)
|
|
{
|
|
int ch;
|
|
char *s;
|
|
Num *num;
|
|
size_t siz;
|
|
Val v1, v2;
|
|
struct reg *rp;
|
|
|
|
if (setjmp(env))
|
|
return;
|
|
|
|
for (s = input->s; (ch = *s) != '\0'; ++s) {
|
|
if (ch < 0 || ch > UCHAR_MAX || !isspace(ch))
|
|
break;
|
|
}
|
|
input->s = s + (ch != '\0');
|
|
|
|
switch (ch) {
|
|
case '\0':
|
|
break;
|
|
case 'n':
|
|
v1 = pop();
|
|
printval(v1);
|
|
freeval(v1);
|
|
break;
|
|
case 'p':
|
|
v1 = pop();
|
|
printval(v1);
|
|
putchar('\n');
|
|
push(v1);
|
|
break;
|
|
case 'P':
|
|
printbytes();
|
|
break;
|
|
case 'f':
|
|
dumpstack();
|
|
break;
|
|
case '+':
|
|
arith(addnum);
|
|
break;
|
|
case '-':
|
|
arith(subnum);
|
|
break;
|
|
case '*':
|
|
arith(mulnum);
|
|
break;
|
|
case '/':
|
|
arith(divnum);
|
|
break;
|
|
case '%':
|
|
arith(modnum);
|
|
break;
|
|
case '^':
|
|
arith(expnum);
|
|
break;
|
|
case '~':
|
|
pushdivmod();
|
|
break;
|
|
case 'v':
|
|
num = popnum();
|
|
pushnum(sqrtnum(num));
|
|
freenum(num);
|
|
break;
|
|
case 'c':
|
|
clearstack();
|
|
break;
|
|
case 'd':
|
|
dupstack();
|
|
break;
|
|
case 'r':
|
|
needstack(2);
|
|
v1 = pop();
|
|
v2 = pop();
|
|
push(v1);
|
|
push(v2);
|
|
break;
|
|
case 'S':
|
|
pushreg();
|
|
break;
|
|
case 'L':
|
|
popreg();
|
|
break;
|
|
case 's':
|
|
rp = lookup(regname());
|
|
freeval(rp->val);
|
|
rp->val = pop();
|
|
break;
|
|
case 'l':
|
|
rp = lookup(regname());
|
|
push(dupval(rp->val));
|
|
break;
|
|
case 'i':
|
|
setibase();
|
|
break;
|
|
case 'o':
|
|
setobase();
|
|
break;
|
|
case 'k':
|
|
setscale();
|
|
break;
|
|
case 'I':
|
|
pushint(ibase);
|
|
break;
|
|
case 'O':
|
|
pushint(obase);
|
|
break;
|
|
case 'K':
|
|
pushint(scale);
|
|
break;
|
|
case '[':
|
|
pushstr(string(NULL, NULL));
|
|
break;
|
|
case 'x':
|
|
execmacro();
|
|
break;
|
|
case '!':
|
|
switch (*input->s++) {
|
|
case '<':
|
|
ch = GE;
|
|
break;
|
|
case '>':
|
|
ch = LE;
|
|
break;
|
|
case '=':
|
|
ch = NE;
|
|
break;
|
|
default:
|
|
system(input->s-1);
|
|
goto discard;
|
|
}
|
|
case '>':
|
|
case '<':
|
|
case '=':
|
|
relational(ch);
|
|
break;
|
|
case '?':
|
|
s = NULL;
|
|
if (getline(&s, &siz, stdin) > 0) {
|
|
pushstr(s);
|
|
} else {
|
|
free(s);
|
|
if (ferror(stdin))
|
|
eprintf("reading from file\n");
|
|
}
|
|
break;
|
|
case 'q':
|
|
delinput('q', 2);
|
|
break;
|
|
case 'Q':
|
|
delinput('Q', popint());
|
|
break;
|
|
case 'Z':
|
|
pushlen();
|
|
break;
|
|
case 'X':
|
|
pushfrac();
|
|
break;
|
|
case 'z':
|
|
deepstack();
|
|
break;
|
|
case '#':
|
|
discard:
|
|
while (*input->s)
|
|
++input->s;
|
|
break;
|
|
case ':':
|
|
aryset();
|
|
break;
|
|
case ';':
|
|
aryget();
|
|
break;
|
|
default:
|
|
if (!strchr(digits, ch))
|
|
error("'%c' (%#o) unimplemented", ch, ch);
|
|
case '_':
|
|
--input->s;
|
|
pushnum(tonum());
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dc(char *fname)
|
|
{
|
|
FILE *fp;
|
|
|
|
if (strcmp(fname, "-") == 0) {
|
|
fp = stdin;
|
|
} else {
|
|
if ((fp = fopen(fname, "r")) == NULL)
|
|
eprintf("opening '%s':", fname);
|
|
}
|
|
addinput(fp, NULL);
|
|
|
|
while (moreinput())
|
|
eval();
|
|
|
|
fclose(fp);
|
|
free(input);
|
|
input = NULL;
|
|
}
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
eprintf("usage: dc [-i] [file ...]\n");
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
ARGBEGIN {
|
|
case 'i':
|
|
iflag = 1;
|
|
break;
|
|
default:
|
|
usage();
|
|
} ARGEND
|
|
|
|
while (*argv)
|
|
dc(*argv++);
|
|
dc("-");
|
|
|
|
return 0;
|
|
}
|