add pc(1)
This commit is contained in:
parent
43bb71c8cc
commit
a931ad737a
3 changed files with 938 additions and 2 deletions
132
sys/man/1/pc
Normal file
132
sys/man/1/pc
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
.TH PC 1
|
||||||
|
.SH NAME
|
||||||
|
pc \- programmer's calculator
|
||||||
|
.SH SYNOPSYS
|
||||||
|
.B pc
|
||||||
|
[
|
||||||
|
.B -n
|
||||||
|
]
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.I Pc
|
||||||
|
is an arbitrary precision calculator with a special emphasis on supporting two's complement bit operations and working with different number bases.
|
||||||
|
.PP
|
||||||
|
.I Pc
|
||||||
|
reads input statements which are either expressions or control statements.
|
||||||
|
Multiple statements in one line can be separated by semicolons.
|
||||||
|
.I Pc
|
||||||
|
prints the value of all expressions that are not terminated by a semicolon.
|
||||||
|
.PP
|
||||||
|
Expressions can use the C-like operators
|
||||||
|
.TP
|
||||||
|
.B + - * ** \fR(exponentiation\fR)
|
||||||
|
.TP
|
||||||
|
.B / % \fR(Euclidean division, by default\fR)
|
||||||
|
.TP
|
||||||
|
.B "& | ^ ~ ! << >>"
|
||||||
|
.TP
|
||||||
|
.B "&& || \fR(returning the second argument, if appropriate)"
|
||||||
|
.TP
|
||||||
|
.B < >= < <= == !=
|
||||||
|
.PP
|
||||||
|
Variables can be defined using
|
||||||
|
.BR = .
|
||||||
|
The builtin variable
|
||||||
|
.B @
|
||||||
|
always refers to the last printed result.
|
||||||
|
.PP
|
||||||
|
Numbers can use the prefixes
|
||||||
|
.B 0b
|
||||||
|
(binary),
|
||||||
|
.B 0
|
||||||
|
(octal),
|
||||||
|
.B 0d
|
||||||
|
(decimal) and
|
||||||
|
.B 0x
|
||||||
|
(hexadecimal).
|
||||||
|
.B _
|
||||||
|
in numbers can be added for readability and is ignored.
|
||||||
|
.SS Builtin functions
|
||||||
|
.TF xtend(n,m)
|
||||||
|
.TP
|
||||||
|
.I bin(n)
|
||||||
|
Display \fIn\fR in binary.
|
||||||
|
.TP
|
||||||
|
.I oct(n)
|
||||||
|
Display \fIn\fR in octal.
|
||||||
|
.TP
|
||||||
|
.I dec(n)
|
||||||
|
Display \fIn\fR in decimal.
|
||||||
|
.TP
|
||||||
|
.I hex(n)
|
||||||
|
Display \fIn\fR in hexadecimal.
|
||||||
|
.TP
|
||||||
|
.I abs(n)
|
||||||
|
Absolute value of \fIn\fR.
|
||||||
|
.TP
|
||||||
|
.I round(n,m)
|
||||||
|
\fIn\fR rounded to the nearest multiple of \fIm\fR.
|
||||||
|
Numbers exactly halfway between are rounded to the next even multiple.
|
||||||
|
.TP
|
||||||
|
.I floor(n,m)
|
||||||
|
\fIn\fR rounded down to the next multiple of \fIm\fR.
|
||||||
|
.TP
|
||||||
|
.I ceil(n,m)
|
||||||
|
\fIn\fR rounded up to the next multiple of \fIm\fR.
|
||||||
|
.TP
|
||||||
|
.I trunc(n,m)
|
||||||
|
\fIn\fR truncated to \fIm\fR bits.
|
||||||
|
.TP
|
||||||
|
.I xtend(n,m)
|
||||||
|
\fIn\fR truncated to \fIm\fR bits, with the highest bit interpreted as a sign bit.
|
||||||
|
.TP
|
||||||
|
.I ubits(n)
|
||||||
|
The minimum number of bits required to represent \fIn\fR as an unsigned number.
|
||||||
|
.TP
|
||||||
|
.I sbits(n)
|
||||||
|
The minimum number of bits required to represent \fIn\fR as an signed number.
|
||||||
|
.SS Control statements
|
||||||
|
.PP
|
||||||
|
Control statements are always evaluated with default input base 10.
|
||||||
|
.TP
|
||||||
|
\fL_\fR \fIn\fR
|
||||||
|
If \fIn\fR ≠ 0, insert
|
||||||
|
.B _
|
||||||
|
in all printed numbers, every
|
||||||
|
.I n
|
||||||
|
digits.
|
||||||
|
.TP
|
||||||
|
\fL<\fR \fIn\fR
|
||||||
|
Set the default input base to \fIn\fR (default 10).
|
||||||
|
The input base can always be overriden by the base prefixes defined above.
|
||||||
|
.TP
|
||||||
|
\fL>\fR \fIn\fR
|
||||||
|
Set the output base to \fIn\fR.
|
||||||
|
If \fIn\fR = 0 (default), print each number in the base it was input in.
|
||||||
|
.TP
|
||||||
|
\fL/\fR 0
|
||||||
|
Use Euclidean division (default).
|
||||||
|
\fIa\fR / \fIb\fR is rounded towards ±∞ (opposite sign as \fIb\fR).
|
||||||
|
\fIa\fR % \fIb\fR is always non-negative.
|
||||||
|
.TP
|
||||||
|
\fL/\fR 1
|
||||||
|
Use truncating division (same as C).
|
||||||
|
\fIa\fR / \fIb\fR is rounded towards zero.
|
||||||
|
\fIa\fR % \fIb\fR can be negative.
|
||||||
|
.SH SOURCE
|
||||||
|
.B /sys/src/cmd/pc.y
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.IR bc (1),
|
||||||
|
.IR hoc (1)
|
||||||
|
.SH BUGS
|
||||||
|
With the input base set to 16, terms such as
|
||||||
|
.B ABC
|
||||||
|
are ambiguous.
|
||||||
|
They are interpreted as numbers only if there is no function or variable of the same name.
|
||||||
|
To force interpretation as a number, use the \fL0x\fR prefix.
|
||||||
|
.PP
|
||||||
|
Arbitrary bases should be supported, but are not supported by the
|
||||||
|
.IR mp (2)
|
||||||
|
string functions.
|
||||||
|
.SH HISTORY
|
||||||
|
.I Pc
|
||||||
|
first appeared in 9front (August, 2016).
|
|
@ -48,7 +48,7 @@ all:V: $PROGS dirs
|
||||||
&:n: $O.&
|
&:n: $O.&
|
||||||
mv $O.$stem $stem
|
mv $O.$stem $stem
|
||||||
|
|
||||||
%.tab.h %.tab.c: %.y
|
%.tab.h %.tab.c:D: %.y
|
||||||
$YACC $YFLAGS -s $stem $prereq
|
$YACC $YFLAGS -s $stem $prereq
|
||||||
|
|
||||||
%.install:V: $BIN/%
|
%.install:V: $BIN/%
|
||||||
|
@ -108,7 +108,7 @@ installall:V:
|
||||||
%.acid: %.$O $HFILES
|
%.acid: %.$O $HFILES
|
||||||
$CC $CFLAGS -a $stem.c >$target
|
$CC $CFLAGS -a $stem.c >$target
|
||||||
|
|
||||||
(bc|units|mpc).c:R: \1.tab.c
|
(bc|units|mpc|pc).c:R: \1.tab.c
|
||||||
mv $stem1.tab.c $stem1.c
|
mv $stem1.tab.c $stem1.c
|
||||||
|
|
||||||
$BIN/init: $O.init
|
$BIN/init: $O.init
|
||||||
|
|
804
sys/src/cmd/pc.y
Normal file
804
sys/src/cmd/pc.y
Normal file
|
@ -0,0 +1,804 @@
|
||||||
|
%{
|
||||||
|
#include <u.h>
|
||||||
|
#include <libc.h>
|
||||||
|
#include <bio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <mp.h>
|
||||||
|
#include <pool.h>
|
||||||
|
#include <thread.h>
|
||||||
|
|
||||||
|
int inbase = 10, outbase, divmode, sep, fail, prompt;
|
||||||
|
enum { MAXARGS = 16 };
|
||||||
|
|
||||||
|
typedef struct Num Num;
|
||||||
|
struct Num {
|
||||||
|
mpint;
|
||||||
|
int b;
|
||||||
|
Ref;
|
||||||
|
};
|
||||||
|
enum { STRONG = 0x100 };
|
||||||
|
|
||||||
|
void *
|
||||||
|
emalloc(int n)
|
||||||
|
{
|
||||||
|
void *v;
|
||||||
|
|
||||||
|
v = malloc(n);
|
||||||
|
if(v == nil)
|
||||||
|
sysfatal("malloc: %r");
|
||||||
|
memset(v, 0, n);
|
||||||
|
setmalloctag(v, getcallerpc(&n));
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
error(char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list va;
|
||||||
|
Fmt f;
|
||||||
|
char buf[256];
|
||||||
|
|
||||||
|
fmtfdinit(&f, 2, buf, sizeof(buf));
|
||||||
|
va_start(va, fmt);
|
||||||
|
fmtvprint(&f, fmt, va);
|
||||||
|
fmtrune(&f, '\n');
|
||||||
|
fmtfdflush(&f);
|
||||||
|
va_end(va);
|
||||||
|
fail++;
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
Num *
|
||||||
|
numalloc(void)
|
||||||
|
{
|
||||||
|
Num *r;
|
||||||
|
|
||||||
|
r = emalloc(sizeof(Num));
|
||||||
|
r->ref = 1;
|
||||||
|
r->p = emalloc(0);
|
||||||
|
mpassign(mpzero, r);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
Num *
|
||||||
|
numincref(Num *n)
|
||||||
|
{
|
||||||
|
incref(n);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
Num *
|
||||||
|
numdecref(Num *n)
|
||||||
|
{
|
||||||
|
if(n == nil) return nil;
|
||||||
|
if(decref(n) == 0){
|
||||||
|
free(n->p);
|
||||||
|
free(n);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
Num *
|
||||||
|
nummod(Num *n)
|
||||||
|
{
|
||||||
|
Num *m;
|
||||||
|
|
||||||
|
if(n == nil) return nil;
|
||||||
|
if(n->ref == 1) return n;
|
||||||
|
m = numalloc();
|
||||||
|
mpassign(n, m);
|
||||||
|
m->b = n->b;
|
||||||
|
numdecref(n);
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
basemax(int a, int b)
|
||||||
|
{
|
||||||
|
if(a == STRONG+10 && b >= STRONG) return b;
|
||||||
|
if(b == STRONG+10 && a >= STRONG) return a;
|
||||||
|
if(a == 10) return b;
|
||||||
|
if(b == 10) return a;
|
||||||
|
if(a < b) return b;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
%}
|
||||||
|
%token LOEXP LOLSH LORSH LOEQ LONE LOLE LOGE LOLAND LOLOR
|
||||||
|
%{
|
||||||
|
|
||||||
|
Num *
|
||||||
|
numbin(int op, Num *a, Num *b)
|
||||||
|
{
|
||||||
|
mpint *r;
|
||||||
|
|
||||||
|
if(fail || a == nil || b == nil) return nil;
|
||||||
|
a = nummod(a);
|
||||||
|
a->b = basemax(a->b, b->b);
|
||||||
|
switch(op){
|
||||||
|
case '+': mpadd(a, b, a); break;
|
||||||
|
case '-': mpsub(a, b, a); break;
|
||||||
|
case '*': mpmul(a, b, a); break;
|
||||||
|
case '/':
|
||||||
|
if(mpcmp(b, mpzero) == 0){
|
||||||
|
numdecref(a);
|
||||||
|
numdecref(b);
|
||||||
|
return error("division by zero");
|
||||||
|
}
|
||||||
|
r = mpnew(0);
|
||||||
|
mpdiv(a, b, a, r);
|
||||||
|
if(!divmode && r->sign < 0)
|
||||||
|
if(b->sign > 0)
|
||||||
|
mpsub(a, mpone, a);
|
||||||
|
else
|
||||||
|
mpadd(a, mpone, a);
|
||||||
|
mpfree(r);
|
||||||
|
break;
|
||||||
|
case '%':
|
||||||
|
if(mpcmp(b, mpzero) == 0){
|
||||||
|
numdecref(a);
|
||||||
|
numdecref(b);
|
||||||
|
return error("division by zero");
|
||||||
|
}
|
||||||
|
mpdiv(a, b, nil, a);
|
||||||
|
if(!divmode && a->sign < 0)
|
||||||
|
if(b->sign > 0)
|
||||||
|
mpadd(a, b, a);
|
||||||
|
else
|
||||||
|
mpsub(a, b, a);
|
||||||
|
break;
|
||||||
|
case '&': mpand(a, b, a); break;
|
||||||
|
case '|': mpor(a, b, a); break;
|
||||||
|
case '^': mpxor(a, b, a); break;
|
||||||
|
case LOEXP:
|
||||||
|
if(mpcmp(b, mpzero) < 0){
|
||||||
|
numdecref(a);
|
||||||
|
numdecref(b);
|
||||||
|
return error("negative exponent");
|
||||||
|
}
|
||||||
|
mpexp(a, b, nil, a);
|
||||||
|
break;
|
||||||
|
case LOLSH:
|
||||||
|
if(mpsignif(b) >= 31){
|
||||||
|
if(b->sign > 0)
|
||||||
|
error("left shift overflow");
|
||||||
|
itomp(-(mpcmp(a, mpzero) < 0), a);
|
||||||
|
}else
|
||||||
|
mpasr(a, -mptoi(b), a);
|
||||||
|
break;
|
||||||
|
case LORSH:
|
||||||
|
if(mpsignif(b) >= 31){
|
||||||
|
if(b->sign < 0)
|
||||||
|
error("right shift overflow");
|
||||||
|
itomp(-(mpcmp(a, mpzero) < 0), a);
|
||||||
|
}else
|
||||||
|
mpasr(a, mptoi(b), a);
|
||||||
|
break;
|
||||||
|
case '<': itomp(mpcmp(a, b) < 0, a); break;
|
||||||
|
case '>': itomp(mpcmp(a, b) > 0, a); break;
|
||||||
|
case LOLE: itomp(mpcmp(a, b) <= 0, a); break;
|
||||||
|
case LOGE: itomp(mpcmp(a, b) >= 0, a); break;
|
||||||
|
case LOEQ: itomp(mpcmp(a, b) == 0, a); break;
|
||||||
|
case LONE: itomp(mpcmp(a, b) != 0, a); break;
|
||||||
|
case LOLAND:
|
||||||
|
a->b = b->b;
|
||||||
|
if(mpcmp(a, mpzero) == 0)
|
||||||
|
mpassign(mpzero, a);
|
||||||
|
else
|
||||||
|
mpassign(b, a);
|
||||||
|
break;
|
||||||
|
case LOLOR:
|
||||||
|
a->b = b->b;
|
||||||
|
if(mpcmp(a, mpzero) != 0)
|
||||||
|
mpassign(mpone, a);
|
||||||
|
else
|
||||||
|
mpassign(b, a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
numdecref(b);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct Symbol Symbol;
|
||||||
|
struct Symbol {
|
||||||
|
enum {
|
||||||
|
SYMNONE,
|
||||||
|
SYMVAR,
|
||||||
|
SYMFUNC,
|
||||||
|
} t;
|
||||||
|
Num *val;
|
||||||
|
int nargs;
|
||||||
|
Num *(*func)(int, Num **);
|
||||||
|
char *name;
|
||||||
|
Symbol *next;
|
||||||
|
};
|
||||||
|
Symbol *symtab[64];
|
||||||
|
|
||||||
|
Symbol *
|
||||||
|
getsym(char *n, int mk)
|
||||||
|
{
|
||||||
|
Symbol **p;
|
||||||
|
for(p = &symtab[*n&63]; *p != nil; p = &(*p)->next)
|
||||||
|
if(strcmp((*p)->name, n) == 0)
|
||||||
|
return *p;
|
||||||
|
if(!mk) return nil;
|
||||||
|
*p = emalloc(sizeof(Symbol));
|
||||||
|
(*p)->name = strdup(n);
|
||||||
|
return *p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
numprint(Num *n)
|
||||||
|
{
|
||||||
|
int b;
|
||||||
|
int l, i;
|
||||||
|
char *s, *t, *p, *q;
|
||||||
|
|
||||||
|
if(n == nil) return;
|
||||||
|
if(n->b >= STRONG || n->b != 0 && outbase == 0)
|
||||||
|
b = n->b & ~STRONG;
|
||||||
|
else if(outbase == 0)
|
||||||
|
b = 10;
|
||||||
|
else
|
||||||
|
b = outbase;
|
||||||
|
s = mptoa(n, b, nil, 0);
|
||||||
|
l = strlen(s);
|
||||||
|
t = emalloc(l * 2 + 4);
|
||||||
|
q = t + l * 2 + 4;
|
||||||
|
*--q = 0;
|
||||||
|
for(p = s + l - 1, i = 0; p >= s && *p != '-'; p--, i++){
|
||||||
|
if(sep != 0 && i == sep){
|
||||||
|
*--q = '_';
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
if(*p >= 'A')
|
||||||
|
*--q = *p + ('a' - 'A');
|
||||||
|
else
|
||||||
|
*--q = *p;
|
||||||
|
}
|
||||||
|
if(mpcmp(n, mpzero) != 0)
|
||||||
|
switch(b){
|
||||||
|
case 16: *--q = 'x'; *--q = '0'; break;
|
||||||
|
case 10: if(outbase != 0 && outbase != 10) {*--q = 'd'; *--q = '0';} break;
|
||||||
|
case 8: *--q = '0'; break;
|
||||||
|
case 2: *--q = 'b'; *--q = '0'; break;
|
||||||
|
}
|
||||||
|
if(p >= s)
|
||||||
|
*--q = '-';
|
||||||
|
print("%s\n", q);
|
||||||
|
free(s);
|
||||||
|
free(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
Num *
|
||||||
|
fncall(Symbol *s, int n, Num **x)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(s->t != SYMFUNC)
|
||||||
|
return error("%s: not a function", s->name);
|
||||||
|
else if(s->nargs >= 0 && s->nargs != n)
|
||||||
|
return error("%s: wrong number of arguments", s->name);
|
||||||
|
for(i = 0; i < n; i++)
|
||||||
|
if(x[i] == nil)
|
||||||
|
return nil;
|
||||||
|
return s->func(n, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
Num *
|
||||||
|
hexfix(Symbol *s)
|
||||||
|
{
|
||||||
|
char *b, *p, *q;
|
||||||
|
|
||||||
|
if(inbase != 16) return nil;
|
||||||
|
if(s->val != nil) return numincref(s->val);
|
||||||
|
if(strspn(s->name, "0123456789ABCDEFabcdef_") != strlen(s->name)) return nil;
|
||||||
|
b = strdup(s->name);
|
||||||
|
for(p = b, q = b; *p != 0; p++)
|
||||||
|
if(*p != '_')
|
||||||
|
*q++ = *p;
|
||||||
|
*q = 0;
|
||||||
|
s->val = numalloc();
|
||||||
|
strtomp(b, nil, 16, s->val);
|
||||||
|
s->val->b = 16;
|
||||||
|
free(b);
|
||||||
|
return numincref(s->val);
|
||||||
|
}
|
||||||
|
|
||||||
|
%}
|
||||||
|
|
||||||
|
%union {
|
||||||
|
Num *n;
|
||||||
|
Symbol *sym;
|
||||||
|
struct {
|
||||||
|
Num *x[MAXARGS];
|
||||||
|
int n;
|
||||||
|
} args;
|
||||||
|
}
|
||||||
|
|
||||||
|
%token <n> LNUM
|
||||||
|
%token <sym> LSYMB
|
||||||
|
|
||||||
|
%type <n> expr
|
||||||
|
%type <args> elist elist1
|
||||||
|
|
||||||
|
%right '='
|
||||||
|
%right '?'
|
||||||
|
%left LOLOR
|
||||||
|
%left LOLAND
|
||||||
|
%left '|'
|
||||||
|
%left '^'
|
||||||
|
%left '&'
|
||||||
|
%left LOEQ LONE
|
||||||
|
%left '<' '>' LOLE LOGE
|
||||||
|
%left LOLSH LORSH
|
||||||
|
%left '+' '-'
|
||||||
|
%left unary
|
||||||
|
%left '*' '/' '%'
|
||||||
|
%right LOEXP
|
||||||
|
|
||||||
|
%{
|
||||||
|
int save;
|
||||||
|
Num *last;
|
||||||
|
Num *lastp;
|
||||||
|
%}
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
input: | input line '\n' {
|
||||||
|
if(!fail && last != nil) {
|
||||||
|
numprint(last);
|
||||||
|
numdecref(lastp);
|
||||||
|
lastp = last;
|
||||||
|
}
|
||||||
|
fail = 0;
|
||||||
|
last = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
line: stat
|
||||||
|
| line ';' stat
|
||||||
|
|
||||||
|
stat: { last = nil; }
|
||||||
|
| expr { last = $1; }
|
||||||
|
| '_' { save = inbase; inbase = 10; } expr {
|
||||||
|
inbase = save;
|
||||||
|
if(mpcmp($3, mpzero) < 0)
|
||||||
|
error("no.");
|
||||||
|
if(!fail)
|
||||||
|
sep = mptoi($3);
|
||||||
|
numdecref($3);
|
||||||
|
numdecref(last);
|
||||||
|
last = nil;
|
||||||
|
}
|
||||||
|
| '<' { save = inbase; inbase = 10; } expr {
|
||||||
|
inbase = save;
|
||||||
|
if(!fail)
|
||||||
|
inbase = mptoi($3);
|
||||||
|
if(inbase != 2 && inbase != 8 && inbase != 10 && inbase != 16){
|
||||||
|
error("no.");
|
||||||
|
inbase = save;
|
||||||
|
}
|
||||||
|
numdecref($3);
|
||||||
|
numdecref(last);
|
||||||
|
last = nil;
|
||||||
|
}
|
||||||
|
| '>' { save = inbase; inbase = 10; } expr {
|
||||||
|
inbase = save;
|
||||||
|
save = outbase;
|
||||||
|
if(!fail)
|
||||||
|
outbase = mptoi($3);
|
||||||
|
if(outbase != 2 && outbase != 8 && outbase != 10 && outbase != 16){
|
||||||
|
error("no.");
|
||||||
|
outbase = save;
|
||||||
|
}
|
||||||
|
numdecref($3);
|
||||||
|
numdecref(last);
|
||||||
|
last = nil;
|
||||||
|
}
|
||||||
|
| '/' { save = inbase; inbase = 10; } expr {
|
||||||
|
inbase = save;
|
||||||
|
save = divmode;
|
||||||
|
if(!fail)
|
||||||
|
divmode = mptoi($3);
|
||||||
|
if(divmode != 0 && divmode != 1){
|
||||||
|
error("no.");
|
||||||
|
divmode = save;
|
||||||
|
}
|
||||||
|
numdecref($3);
|
||||||
|
numdecref(last);
|
||||||
|
last = nil;
|
||||||
|
}
|
||||||
|
| error
|
||||||
|
|
||||||
|
expr: LNUM
|
||||||
|
| '(' expr ')' { $$ = $2; }
|
||||||
|
| expr '+' expr { $$ = numbin('+', $1, $3); }
|
||||||
|
| expr '-' expr { $$ = numbin('-', $1, $3); }
|
||||||
|
| expr '*' expr { $$ = numbin('*', $1, $3); }
|
||||||
|
| expr '/' expr { $$ = numbin('/', $1, $3); }
|
||||||
|
| expr '%' expr { $$ = numbin('%', $1, $3); }
|
||||||
|
| expr '&' expr { $$ = numbin('&', $1, $3); }
|
||||||
|
| expr '|' expr { $$ = numbin('|', $1, $3); }
|
||||||
|
| expr '^' expr { $$ = numbin('^', $1, $3); }
|
||||||
|
| expr LOEXP expr { $$ = numbin(LOEXP, $1, $3); }
|
||||||
|
| expr LOLSH expr { $$ = numbin(LOLSH, $1, $3); }
|
||||||
|
| expr LORSH expr { $$ = numbin(LORSH, $1, $3); }
|
||||||
|
| expr LOEQ expr { $$ = numbin(LOEQ, $1, $3); }
|
||||||
|
| expr LONE expr { $$ = numbin(LONE, $1, $3); }
|
||||||
|
| expr '<' expr { $$ = numbin('<', $1, $3); }
|
||||||
|
| expr '>' expr { $$ = numbin('>', $1, $3); }
|
||||||
|
| expr LOLE expr { $$ = numbin(LOLE, $1, $3); }
|
||||||
|
| expr LOGE expr { $$ = numbin(LOGE, $1, $3); }
|
||||||
|
| expr LOLAND expr { $$ = numbin(LOLAND, $1, $3); }
|
||||||
|
| expr LOLOR expr { $$ = numbin(LOLOR, $1, $3); }
|
||||||
|
| '+' expr %prec unary { $$ = $2; }
|
||||||
|
| '-' expr %prec unary { $$ = nummod($2); if($$ != nil) mpsub(mpzero, $$, $$); }
|
||||||
|
| '~' expr %prec unary { $$ = nummod($2); if($$ != nil) mpnot($$, $$); }
|
||||||
|
| '!' expr %prec unary { $$ = nummod($2); if($$ != nil) itomp(mpcmp($$, mpzero) == 0, $$); }
|
||||||
|
| expr '?' expr ':' expr %prec '?' {
|
||||||
|
if($1 == nil || mpcmp($1, mpzero) != 0){
|
||||||
|
$$ = $3;
|
||||||
|
numdecref($5);
|
||||||
|
}else{
|
||||||
|
$$ = $5;
|
||||||
|
numdecref($3);
|
||||||
|
}
|
||||||
|
numdecref($1);
|
||||||
|
}
|
||||||
|
| LSYMB '(' elist ')' { $$ = fncall($1, $3.n, $3.x); }
|
||||||
|
| LSYMB {
|
||||||
|
Num *n;
|
||||||
|
$$ = nil;
|
||||||
|
switch($1->t){
|
||||||
|
case SYMVAR: $$ = numincref($1->val); break;
|
||||||
|
case SYMNONE:
|
||||||
|
n = hexfix($1);
|
||||||
|
if(n != nil) $$ = n;
|
||||||
|
else error("%s undefined", $1->name);
|
||||||
|
break;
|
||||||
|
case SYMFUNC: error("%s is a function", $1->name); break;
|
||||||
|
default: error("%s invalid here", $1->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
| LSYMB '=' expr {
|
||||||
|
if($1->t != SYMNONE && $1->t != SYMVAR)
|
||||||
|
error("%s redefined", $1->name);
|
||||||
|
else if(!fail){
|
||||||
|
$1->t = SYMVAR;
|
||||||
|
numdecref($1->val);
|
||||||
|
$1->val = numincref($3);
|
||||||
|
}
|
||||||
|
$$ = $3;
|
||||||
|
}
|
||||||
|
| '@' {
|
||||||
|
$$ = lastp;
|
||||||
|
if($$ == nil) error("no last result");
|
||||||
|
else numincref($$);
|
||||||
|
}
|
||||||
|
|
||||||
|
elist: { $$.n = 0; } | elist1
|
||||||
|
elist1: expr { $$.x[0] = $1; $$.n = 1; }
|
||||||
|
| elist1 ',' expr {
|
||||||
|
$$ = $1;
|
||||||
|
if($$.n >= MAXARGS)
|
||||||
|
error("too many arguments");
|
||||||
|
else
|
||||||
|
$$.x[$$.n++] = $3;
|
||||||
|
}
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
typedef struct Keyword Keyword;
|
||||||
|
struct Keyword {
|
||||||
|
char *name;
|
||||||
|
int tok;
|
||||||
|
};
|
||||||
|
|
||||||
|
Keyword ops[] = {
|
||||||
|
"**", LOEXP,
|
||||||
|
"<<", LOLSH,
|
||||||
|
"<=", LOLE,
|
||||||
|
">>", LORSH,
|
||||||
|
">=", LOGE,
|
||||||
|
"==", LOEQ,
|
||||||
|
"&&", LOLAND,
|
||||||
|
"||", LOLOR,
|
||||||
|
"", 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
Keyword *optab[128];
|
||||||
|
|
||||||
|
|
||||||
|
Biobuf *in;
|
||||||
|
int prompted;
|
||||||
|
|
||||||
|
int
|
||||||
|
yylex(void)
|
||||||
|
{
|
||||||
|
int c, b;
|
||||||
|
char buf[512], *p;
|
||||||
|
Keyword *kw;
|
||||||
|
|
||||||
|
if(prompt && !prompted) {print("; "); prompted = 1;}
|
||||||
|
do
|
||||||
|
c = Bgetc(in);
|
||||||
|
while(c != '\n' && isspace(c));
|
||||||
|
if(c == '\n') prompted = 0;
|
||||||
|
if(isdigit(c)){
|
||||||
|
for(p = buf, *p++ = c; c = Bgetc(in), isalnum(c) || c == '_'; )
|
||||||
|
if(p < buf + sizeof(buf) - 1)
|
||||||
|
*p++ = c;
|
||||||
|
*p = 0;
|
||||||
|
Bungetc(in);
|
||||||
|
b = inbase;
|
||||||
|
p = buf;
|
||||||
|
if(*p == '0'){
|
||||||
|
p++;
|
||||||
|
switch(*p++){
|
||||||
|
case 0: p -= 2; break;
|
||||||
|
case 'b': case 'B': b = 2; break;
|
||||||
|
case 'd': case 'D': b = 10; break;
|
||||||
|
case 'x': case 'X': b = 16; break;
|
||||||
|
default: p--; b = 8; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yylval.n = numalloc();
|
||||||
|
strtomp(p, &p, b, yylval.n);
|
||||||
|
if(*p != 0) error("not a number: %s", buf);
|
||||||
|
yylval.n->b = b;
|
||||||
|
return LNUM;
|
||||||
|
}
|
||||||
|
if(isalpha(c) || c >= 0x80 || c == '_'){
|
||||||
|
for(p = buf, *p++ = c; c = Bgetc(in), isalnum(c) || c >= 0x80 || c == '_'; )
|
||||||
|
if(p < buf + sizeof(buf) - 1)
|
||||||
|
*p++ = c;
|
||||||
|
*p = 0;
|
||||||
|
Bungetc(in);
|
||||||
|
if(buf[0] == '_' && buf[1] == 0) return '_';
|
||||||
|
yylval.sym = getsym(buf, 1);
|
||||||
|
return LSYMB;
|
||||||
|
}
|
||||||
|
if(c < 128 && (kw = optab[c], kw != nil)){
|
||||||
|
b = Bgetc(in);
|
||||||
|
for(; kw->name[0] == c; kw++)
|
||||||
|
if(kw->name[0] == b)
|
||||||
|
return kw->tok;
|
||||||
|
Bungetc(in);
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
yyerror(char *msg)
|
||||||
|
{
|
||||||
|
error("%s", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
regfunc(char *n, Num *(*f)(int, Num **), int nargs)
|
||||||
|
{
|
||||||
|
Symbol *s;
|
||||||
|
|
||||||
|
s = getsym(n, 1);
|
||||||
|
s->t = SYMFUNC;
|
||||||
|
s->func = f;
|
||||||
|
s->nargs = nargs;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
toint(Num *n, int *p, int mustpos)
|
||||||
|
{
|
||||||
|
if(mpsignif(n) > 31 || mustpos && mpcmp(n, mpzero) < 0){
|
||||||
|
error("invalid argument");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(p != nil)
|
||||||
|
*p = mptoi(n);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Num *
|
||||||
|
fnhex(int, Num **a)
|
||||||
|
{
|
||||||
|
Num *r;
|
||||||
|
|
||||||
|
r = nummod(a[0]);
|
||||||
|
r->b = STRONG | 16;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
Num *
|
||||||
|
fndec(int, Num **a)
|
||||||
|
{
|
||||||
|
Num *r;
|
||||||
|
|
||||||
|
r = nummod(a[0]);
|
||||||
|
r->b = STRONG | 10;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
Num *
|
||||||
|
fnoct(int, Num **a)
|
||||||
|
{
|
||||||
|
Num *r;
|
||||||
|
|
||||||
|
r = nummod(a[0]);
|
||||||
|
r->b = STRONG | 8;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
Num *
|
||||||
|
fnbin(int, Num **a)
|
||||||
|
{
|
||||||
|
Num *r;
|
||||||
|
|
||||||
|
r = nummod(a[0]);
|
||||||
|
r->b = STRONG | 2;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
Num *
|
||||||
|
fnabs(int, Num **a)
|
||||||
|
{
|
||||||
|
Num *r;
|
||||||
|
|
||||||
|
r = nummod(a[0]);
|
||||||
|
r->sign = 1;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
Num *
|
||||||
|
fnround(int, Num **a)
|
||||||
|
{
|
||||||
|
mpint *q, *r;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(mpcmp(a[1], mpzero) <= 0){
|
||||||
|
numdecref(a[0]);
|
||||||
|
numdecref(a[1]);
|
||||||
|
return error("invalid argument");
|
||||||
|
}
|
||||||
|
q = mpnew(0);
|
||||||
|
r = mpnew(0);
|
||||||
|
a[0] = nummod(a[0]);
|
||||||
|
mpdiv(a[0], a[1], q, r);
|
||||||
|
if(r->sign < 0) mpadd(r, a[1], r);
|
||||||
|
mpleft(r, 1, r);
|
||||||
|
i = mpcmp(r, a[1]);
|
||||||
|
mpright(r, 1, r);
|
||||||
|
if(i > 0 || i == 0 && (a[0]->sign < 0) ^ (q->top != 0 && (q->p[0] & 1) != 0))
|
||||||
|
mpsub(r, a[1], r);
|
||||||
|
mpsub(a[0], r, a[0]);
|
||||||
|
mpfree(q);
|
||||||
|
mpfree(r);
|
||||||
|
numdecref(a[1]);
|
||||||
|
return a[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
Num *
|
||||||
|
fnfloor(int, Num **a)
|
||||||
|
{
|
||||||
|
mpint *r;
|
||||||
|
|
||||||
|
if(mpcmp(a[1], mpzero) <= 0){
|
||||||
|
numdecref(a[0]);
|
||||||
|
numdecref(a[1]);
|
||||||
|
return error("invalid argument");
|
||||||
|
}
|
||||||
|
r = mpnew(0);
|
||||||
|
a[0] = nummod(a[0]);
|
||||||
|
mpdiv(a[0], a[1], nil, r);
|
||||||
|
if(r->sign < 0) mpadd(r, a[1], r);
|
||||||
|
mpsub(a[0], r, a[0]);
|
||||||
|
mpfree(r);
|
||||||
|
numdecref(a[1]);
|
||||||
|
return a[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
Num *
|
||||||
|
fnceil(int, Num **a)
|
||||||
|
{
|
||||||
|
mpint *r;
|
||||||
|
|
||||||
|
if(mpcmp(a[1], mpzero) <= 0){
|
||||||
|
numdecref(a[0]);
|
||||||
|
numdecref(a[1]);
|
||||||
|
return error("invalid argument");
|
||||||
|
}
|
||||||
|
r = mpnew(0);
|
||||||
|
a[0] = nummod(a[0]);
|
||||||
|
mpdiv(a[0], a[1], nil, r);
|
||||||
|
if(r->sign < 0) mpadd(r, a[1], r);
|
||||||
|
if(mpcmp(r, mpzero) != 0){
|
||||||
|
mpsub(a[0], r, a[0]);
|
||||||
|
mpadd(a[0], a[1], a[0]);
|
||||||
|
}
|
||||||
|
mpfree(r);
|
||||||
|
numdecref(a[1]);
|
||||||
|
return a[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
Num *
|
||||||
|
fntrunc(int, Num **a)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(toint(a[1], &i, 1)){
|
||||||
|
numdecref(a[0]);
|
||||||
|
numdecref(a[1]);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
mptrunc(a[0], i, a[0]);
|
||||||
|
return a[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
Num *
|
||||||
|
fnxtend(int, Num **a)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(toint(a[1], &i, 1)) return nil;
|
||||||
|
mpxtend(a[0], i, a[0]);
|
||||||
|
return a[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
Num *
|
||||||
|
fnubits(int, Num **a)
|
||||||
|
{
|
||||||
|
if(a[0]->sign < 0){
|
||||||
|
numdecref(a[0]);
|
||||||
|
return error("invalid argument");
|
||||||
|
}
|
||||||
|
a[0] = nummod(a[0]);
|
||||||
|
itomp(mpsignif(a[0]), a[0]);
|
||||||
|
a[0]->b = 10;
|
||||||
|
return a[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
Num *
|
||||||
|
fnsbits(int, Num **a)
|
||||||
|
{
|
||||||
|
a[0] = nummod(a[0]);
|
||||||
|
if(a[0]->sign < 0) mpadd(a[0], mpone, a[0]);
|
||||||
|
itomp(mpsignif(a[0]) + 1, a[0]);
|
||||||
|
a[0]->b = 10;
|
||||||
|
return a[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
Keyword *kw;
|
||||||
|
|
||||||
|
fmtinstall('B', mpfmt);
|
||||||
|
|
||||||
|
for(kw = ops; kw->name[0] != 0; kw++)
|
||||||
|
if(optab[kw->name[0]] == nil)
|
||||||
|
optab[kw->name[0]] = kw;
|
||||||
|
|
||||||
|
regfunc("hex", fnhex, 1);
|
||||||
|
regfunc("dec", fndec, 1);
|
||||||
|
regfunc("oct", fnoct, 1);
|
||||||
|
regfunc("bin", fnbin, 1);
|
||||||
|
regfunc("abs", fnabs, 1);
|
||||||
|
regfunc("round", fnround, 2);
|
||||||
|
regfunc("floor", fnfloor, 2);
|
||||||
|
regfunc("ceil", fnceil, 2);
|
||||||
|
regfunc("trunc", fntrunc, 2);
|
||||||
|
regfunc("xtend", fnxtend, 2);
|
||||||
|
regfunc("ubits", fnubits, 1);
|
||||||
|
regfunc("sbits", fnsbits, 1);
|
||||||
|
|
||||||
|
prompt = 1;
|
||||||
|
ARGBEGIN{
|
||||||
|
case 'n': prompt = 0; break;
|
||||||
|
}ARGEND;
|
||||||
|
|
||||||
|
in = Bfdopen(0, OREAD);
|
||||||
|
if(in == nil) sysfatal("Bfdopen: %r");
|
||||||
|
extern void yyparse(void);
|
||||||
|
yyparse();
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue