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.&
|
||||
mv $O.$stem $stem
|
||||
|
||||
%.tab.h %.tab.c: %.y
|
||||
%.tab.h %.tab.c:D: %.y
|
||||
$YACC $YFLAGS -s $stem $prereq
|
||||
|
||||
%.install:V: $BIN/%
|
||||
|
@ -108,7 +108,7 @@ installall:V:
|
|||
%.acid: %.$O $HFILES
|
||||
$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
|
||||
|
||||
$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…
Reference in a new issue