ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/cmd/calc.b/
implement Calc;
include "sys.m";
sys: Sys;
include "draw.m";
include "arg.m";
arg: Arg;
include "bufio.m";
bufio: Bufio;
Iobuf: import bufio;
include "math.m";
maths: Math;
include "rand.m";
rand: Rand;
include "daytime.m";
daytime: Daytime;
Calc: module
{
init: fn(nil: ref Draw->Context, argv: list of string);
};
init(nil: ref Draw->Context, args: list of string)
{
sys = load Sys Sys->PATH;
arg = load Arg Arg->PATH;
bufio = load Bufio Bufio->PATH;
maths = load Math Math->PATH;
rand = load Rand Rand->PATH;
daytime = load Daytime Daytime->PATH;
maths->FPcontrol(0, Math->INVAL|Math->ZDIV|Math->OVFL|Math->UNFL|Math->INEX);
rand->init(daytime->now());
rand->init(rand->rand(Big)^rand->rand(Big));
daytime = nil;
arg->init(args);
while((c := arg->opt()) != 0){
case(c){
'b' =>
bits = 1;
'd' =>
debug = 1;
's' =>
strict = 1;
}
}
gargs = args = arg->argv();
if(args == nil){
stdin = 1;
bin = bufio->fopen(sys->fildes(0), Sys->OREAD);
}
else if(tl args == nil)
bin = bufio->open(hd args, Sys->OREAD);
syms = array[Hash] of ref Sym;
pushscope();
for(i := 0; keyw[i].t0 != nil; i++)
enter(keyw[i].t0, keyw[i].t1);
for(i = 0; conw[i].t0 != nil; i++)
adddec(conw[i].t0, Ocon, conw[i].t1, 0);
for(i = 0; varw[i].t0 != nil; i++)
adddec(varw[i].t0, Ovar, varw[i].t1, 0);
for(i = 0; funw[i].t0 != nil; i++)
adddec(funw[i].t0, Olfun, real funw[i].t1, funw[i].t2);
deg = lookup(Deg).dec;
pbase = lookup(Base).dec;
errdec = ref Dec;
pushscope();
for(;;){
e: ref Node;
{
t := lex();
if(t == Oeof)
break;
unlex(t);
ls := lexes;
e = stat(1);
ckstat(e, Onothing, 0);
if(ls == lexes){
t = lex();
error(nil, sys->sprint("syntax error near %s", opstring(t)));
unlex(t);
}
consume(Onl);
}
exception ex{
Eeof =>
e = nil;
err("premature eof");
skip();
"*" =>
e = nil;
err(ex);
skip();
}
if(0 && debug)
prtree(e, 0);
if(e != nil && e.op != Ofn){
(k, v) := (Onothing, 0.0);
{
(k, v) = estat(e);
}
exception ex{
"*" =>
e = nil;
err(ex);
}
if(pexp(e))
printnum(v, "\n");
if(k == Oexit)
exit;
}
}
popscope();
popscope();
}
bits: int;
debug: int;
strict: int;
None: con -2;
Eof: con -1;
Eeof: con "eof";
Hash: con 16;
Big: con 1<<30;
Maxint: con 16r7FFFFFFF;
Nan: con Math->NaN;
Infinity: con Math->Infinity;
Pi: con Math->Pi;
Eps: con 1E-10;
Bigeps: con 1E-2;
Ln2: con 0.6931471805599453;
Ln10: con 2.302585092994046;
Euler: con 2.71828182845904523536;
Gamma: con 0.57721566490153286060;
Phi: con 1.61803398874989484820;
Oeof,
Ostring, Onum, Oident, Ocon, Ovar, Ofun, Olfun,
Oadd, Osub, Omul, Odiv, Omod, Oidiv, Oexp, Oand, Oor, Oxor, Olsh, Orsh,
Oadde, Osube, Omule, Odive, Omode, Oidive, Oexpe, Oande, Oore, Oxore, Olshe, Orshe,
Oeq, One, Ogt, Olt, Oge, Ole,
Oinc, Opreinc, Opostinc, Odec, Opredec, Opostdec,
Oandand, Ooror,
Oexc, Onot, Ofact, Ocom,
Oas, Odas,
Oplus, Ominus, Oinv,
Ocomma, Oscomma, Oquest, Ocolon,
Onand, Onor, Oimp, Oimpby, Oiff,
Olbr, Orbr, Olcbr, Orcbr, Oscolon, Onl,
Onothing,
Oprint, Oread,
Oif, Oelse, Ofor, Owhile, Odo, Obreak, Ocont, Oexit, Oret, Ofn, Oinclude,
Osigma, Opi, Ocfrac, Oderiv, Ointeg, Osolve,
Olog, Olog10, Olog2, Ologb, Oexpf, Opow, Osqrt, Ocbrt, Ofloor, Oceil, Omin, Omax, Oabs, Ogamma, Osign, Oint, Ofrac, Oround, Oerf, Oatan2, Osin, Ocos, Otan, Oasin, Oacos, Oatan, Osinh, Ocosh, Otanh, Oasinh, Oacosh, Oatanh, Orand,
Olast: con iota;
Binary: con (1<<8);
Preunary: con (1<<9);
Postunary: con (1<<10);
Assoc: con (1<<11);
Rassoc: con (1<<12);
Prec: con Binary-1;
opss := array[Olast] of
{
"eof",
"string",
"number",
"identifier",
"constant",
"variable",
"function",
"library function",
"+",
"-",
"*",
"/",
"%",
"//",
"&",
"|",
"^",
"<<",
">>",
"+=",
"-=",
"*=",
"/=",
"%=",
"//=",
"&=",
"|=",
"^=",
"<<=",
">>=",
"==",
"!=",
">",
"<",
">=",
"<=",
"++",
"++",
"++",
"--",
"--",
"--",
"**",
"&&",
"||",
"!",
"!",
"!",
"~",
"=",
":=",
"+",
"-",
"1/",
",",
",",
"?",
":",
"↑",
"↓",
"->",
"<-",
"<->",
"(",
")",
"{",
"}",
";",
"\n",
"",
};
ops := array[Olast] of
{
Oeof => 0,
Ostring => 17,
Onum => 17,
Oident => 17,
Ocon => 17,
Ovar => 17,
Ofun => 17,
Olfun => 17,
Oadd => 12|Binary|Assoc|Preunary,
Osub => 12|Binary|Preunary,
Omul => 13|Binary|Assoc,
Odiv => 13|Binary,
Omod => 13|Binary,
Oidiv => 13|Binary,
Oexp => 14|Binary|Rassoc,
Oand => 8|Binary|Assoc,
Oor => 6|Binary|Assoc,
Oxor => 7|Binary|Assoc,
Olsh => 11|Binary,
Orsh => 11|Binary,
Oadde => 2|Binary|Rassoc,
Osube => 2|Binary|Rassoc,
Omule => 2|Binary|Rassoc,
Odive => 2|Binary|Rassoc,
Omode => 2|Binary|Rassoc,
Oidive => 2|Binary|Rassoc,
Oexpe => 2|Binary|Rassoc,
Oande => 2|Binary|Rassoc,
Oore => 2|Binary|Rassoc,
Oxore => 2|Binary|Rassoc,
Olshe => 2|Binary|Rassoc,
Orshe => 2|Binary|Rassoc,
Oeq => 9|Binary,
One => 9|Binary,
Ogt => 10|Binary,
Olt => 10|Binary,
Oge => 10|Binary,
Ole => 10|Binary,
Oinc => 15|Rassoc|Preunary|Postunary,
Opreinc => 15|Rassoc|Preunary,
Opostinc => 15|Rassoc|Postunary,
Odec => 15|Rassoc|Preunary|Postunary,
Opredec => 15|Rassoc|Preunary,
Opostdec => 15|Rassoc|Postunary,
Oandand => 5|Binary|Assoc,
Ooror => 4|Binary|Assoc,
Oexc => 15|Rassoc|Preunary|Postunary,
Onot => 15|Rassoc|Preunary,
Ofact => 15|Rassoc|Postunary,
Ocom => 15|Rassoc|Preunary,
Oas => 2|Binary|Rassoc,
Odas => 2|Binary|Rassoc,
Oplus => 15|Rassoc|Preunary,
Ominus => 15|Rassoc|Preunary,
Oinv => 15|Rassoc|Postunary,
Ocomma => 1|Binary|Assoc,
Oscomma => 1|Binary|Assoc,
Oquest => 3|Binary|Rassoc,
Ocolon => 3|Binary|Rassoc,
Onand => 8|Binary,
Onor => 6|Binary,
Oimp => 9|Binary,
Oimpby => 9|Binary,
Oiff => 10|Binary|Assoc,
Olbr => 16,
Orbr => 16,
Onothing => 0,
};
Deg: con "degrees";
Base: con "printbase";
Limit: con "solvelimit";
Step: con "solvestep";
keyw := array[] of
{
("include", Oinclude),
("if", Oif),
("else", Oelse),
("for", Ofor),
("while", Owhile),
("do", Odo),
("break", Obreak),
("continue", Ocont),
("exit", Oexit),
("return", Oret),
("print", Oprint),
("read", Oread),
("fn", Ofn),
("", 0),
};
conw := array[] of
{
("π", Pi),
("Pi", Pi),
("e", Euler),
("γ", Gamma),
("Gamma", Gamma),
("φ", Phi),
("Phi", Phi),
("∞", Infinity),
("Infinity", Infinity),
("NaN", Nan),
("Nan", Nan),
("nan", Nan),
("", 0.0),
};
varw := array[] of
{
(Deg, 0.0),
(Base, 10.0),
(Limit, 100.0),
(Step, 1.0),
("", 0.0),
};
funw := array[] of
{
("log", Olog, 1),
("ln", Olog, 1),
("log10", Olog10, 1),
("log2", Olog2, 1),
("logb", Ologb, 2),
("exp", Oexpf, 1),
("pow", Opow, 2),
("sqrt", Osqrt, 1),
("cbrt", Ocbrt, 1),
("floor", Ofloor, 1),
("ceiling", Oceil, 1),
("min", Omin, 2),
("max", Omax, 2),
("abs", Oabs, 1),
("Γ", Ogamma, 1),
("gamma", Ogamma, 1),
("sign", Osign, 1),
("int", Oint, 1),
("frac", Ofrac, 1),
("round", Oround, 1),
("erf", Oerf, 1),
("atan2", Oatan2, 2),
("sin", Osin, 1),
("cos", Ocos, 1),
("tan", Otan, 1),
("asin", Oasin, 1),
("acos", Oacos, 1),
("atan", Oatan, 1),
("sinh", Osinh, 1),
("cosh", Ocosh, 1),
("tanh", Otanh, 1),
("asinh", Oasinh, 1),
("acosh", Oacosh, 1),
("atanh", Oatanh, 1),
("rand", Orand, 0),
("Σ", Osigma, 3),
("sigma", Osigma, 3),
("Π", Opi, 3),
("pi", Opi, 3),
("cfrac", Ocfrac, 3),
("Δ", Oderiv, 2),
("differential", Oderiv, 2),
("∫", Ointeg, 3),
("integral", Ointeg, 3),
("solve", Osolve, 1),
("", 0, 0),
};
stdin: int;
bin: ref Iobuf;
lineno: int = 1;
file: string;
iostack: list of (int, int, int, string, ref Iobuf);
geof: int;
garg: string;
gargs: list of string;
bufc: int = None;
buft: int = Olast;
lexes: int;
lexval: real;
lexstr: string;
lexsym: ref Sym;
syms: array of ref Sym;
deg: ref Dec;
pbase: ref Dec;
errdec: ref Dec;
inloop: int;
infn: int;
Node: adt
{
op: int;
left: cyclic ref Node;
right: cyclic ref Node;
val: real;
str: string;
dec: cyclic ref Dec;
src: int;
};
Dec: adt
{
kind: int;
scope: int;
sym: cyclic ref Sym;
val: real;
na: int;
code: cyclic ref Node;
old: cyclic ref Dec;
next: cyclic ref Dec;
};
Sym: adt
{
name: string;
kind: int;
dec: cyclic ref Dec;
next: cyclic ref Sym;
};
opstring(t: int): string
{
s := opss[t];
if(s != nil)
return s;
for(i := 0; keyw[i].t0 != nil; i++)
if(t == keyw[i].t1)
return keyw[i].t0;
for(i = 0; funw[i].t0 != nil; i++)
if(t == funw[i].t1)
return funw[i].t0;
return s;
}
err(s: string)
{
sys->print("error: %s\n", s);
}
error(n: ref Node, s: string)
{
if(n != nil)
lno := n.src;
else
lno = lineno;
s = sys->sprint("line %d: %s", lno, s);
if(file != nil)
s = sys->sprint("file %s: %s", file, s);
raise s;
}
fatal(s: string)
{
sys->print("fatal: %s\n", s);
exit;
}
stack(s: string, f: ref Iobuf)
{
iostack = (bufc, buft, lineno, file, bin) :: iostack;
bufc = None;
buft = Olast;
lineno = 1;
file = s;
bin = f;
}
unstack()
{
(bufc, buft, lineno, file, bin) = hd iostack;
iostack = tl iostack;
}
doinclude(s: string)
{
f := bufio->open(s, Sys->OREAD);
if(f == nil)
error(nil, sys->sprint("cannot open %s", s));
stack(s, f);
}
getc(): int
{
if((c := bufc) != None)
bufc = None;
else if(bin != nil)
c = bin.getc();
else{
if(garg == nil){
if(gargs == nil){
if(geof == 0){
geof = 1;
c = '\n';
}
else
c = Eof;
}
else{
garg = hd gargs;
gargs = tl gargs;
c = ' ';
}
}
else{
c = garg[0];
garg = garg[1: ];
}
}
if(c == Eof && iostack != nil){
unstack();
return getc();
}
return c;
}
ungetc(c: int)
{
bufc = c;
}
slash(c: int): int
{
if(c != '\\')
return c;
nc := getc();
case(nc){
'b' => return '\b';
'f' => return '\f';
'n' => return '\n';
'r' => return '\r';
't' => return '\t';
}
return nc;
}
lexstring(): int
{
sp := "";
while((c := getc()) != '"'){
if(c == Eof)
raise Eeof;
sp[len sp] = slash(c);
}
lexstr = sp;
return Ostring;
}
lexchar(): int
{
while((c := getc()) != '\''){
if(c == Eof)
raise Eeof;
lexval = real slash(c);
}
return Onum;
}
basev(c: int, base: int): int
{
if(c >= 'a' && c <= 'z')
c += 10-'a';
else if(c >= 'A' && c <= 'Z')
c += 10-'A';
else if(c >= '0' && c <= '9')
c -= '0';
else
return -1;
if(c >= base)
error(nil, "bad digit");
return c;
}
lexe(base: int): int
{
neg := 0;
v := big 0;
c := getc();
if(c == '-')
neg = 1;
else
ungetc(c);
for(;;){
c = getc();
cc := basev(c, base);
if(cc < 0){
ungetc(c);
break;
}
v = big base*v+big cc;
}
if(neg)
v = -v;
return int v;
}
lexnum(): int
{
base := 10;
exp := 0;
r := f := e := 0;
v := big 0;
c := getc();
if(c == '0'){
base = 8;
c = getc();
if(c == '.'){
base = 10;
ungetc(c);
}
else if(c == 'x' || c == 'X')
base = 16;
else
ungetc(c);
}
else
ungetc(c);
for(;;){
c = getc();
if(!r && (c == 'r' || c == 'R')){
if(f || e)
error(nil, "bad base");
r = 1;
base = int v;
if(base < 2 || base > 36)
error(nil, "bad base");
v = big 0;
continue;
}
if(c == '.'){
if(f || e)
error(nil, "bad real");
f = 1;
continue;
}
if(base == 10 && (c == 'e' || c == 'E')){
if(e)
error(nil, "bad E part");
e = 1;
exp = lexe(base);
continue;
}
cc := basev(c, base);
if(cc < 0){
ungetc(c);
break;
}
v = big base*v+big cc;
if(f)
f++;
}
lexval = real v;
if(f)
lexval /= real base**(f-1);
if(exp){
if(exp > 0)
lexval *= real base**exp;
else
lexval *= maths->pow(real base, real exp);
}
return Onum;
}
lexid(): int
{
sp := "";
for(;;){
c := getc();
if(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c >= 'α' && c <= 'ω' || c >= 'Α' && c <= 'Ω' || c == '_')
sp[len sp] = c;
else{
ungetc(c);
break;
}
}
lexsym = enter(sp, Oident);
return lexsym.kind;
}
follow(c: int, c1: int, c2: int): int
{
nc := getc();
if(nc == c)
return c1;
ungetc(nc);
return c2;
}
skip()
{
if((t := buft) != Olast){
lex();
if(t == Onl)
return;
}
for(;;){
c := getc();
if(c == Eof){
ungetc(c);
return;
}
if(c == '\n'){
lineno++;
return;
}
}
}
lex(): int
{
lexes++;
if((t := buft) != Olast){
buft = Olast;
if(t == Onl)
lineno++;
return t;
}
for(;;){
case(c := getc()){
Eof =>
return Oeof;
'#' =>
while((c = getc()) != '\n'){
if(c == Eof)
raise Eeof;
}
lineno++;
'\n' =>
lineno++;
return Onl;
' ' or
'\t' or
'\r' or
'\v' =>
;
'"' =>
return lexstring();
'\'' =>
return lexchar();
'0' to '9' =>
ungetc(c);
return lexnum();
'a' to 'z' or
'A' to 'Z' or
'α' to 'ω' or
'Α' to 'Ω' or
'_' =>
ungetc(c);
return lexid();
'+' =>
c = getc();
if(c == '=')
return Oadde;
ungetc(c);
return follow('+', Oinc, Oadd);
'-' =>
c = getc();
if(c == '=')
return Osube;
if(c == '>')
return Oimp;
ungetc(c);
return follow('-', Odec, Osub);
'*' =>
c = getc();
if(c == '=')
return Omule;
if(c == '*')
return follow('=', Oexpe, Oexp);
ungetc(c);
return Omul;
'/' =>
c = getc();
if(c == '=')
return Odive;
if(c == '/')
return follow('=', Oidive, Oidiv);
ungetc(c);
return Odiv;
'%' =>
return follow('=', Omode, Omod);
'&' =>
c = getc();
if(c == '=')
return Oande;
ungetc(c);
return follow('&', Oandand, Oand);
'|' =>
c = getc();
if(c == '=')
return Oore;
ungetc(c);
return follow('|', Ooror, Oor);
'^' =>
return follow('=', Oxore, Oxor);
'=' =>
return follow('=', Oeq, Oas);
'!' =>
return follow('=', One, Oexc);
'>' =>
c = getc();
if(c == '=')
return Oge;
if(c == '>')
return follow('=', Orshe, Orsh);
ungetc(c);
return Ogt;
'<' =>
c = getc();
if(c == '=')
return Ole;
if(c == '<')
return follow('=', Olshe, Olsh);
if(c == '-')
return follow('>', Oiff, Oimpby);
ungetc(c);
return Olt;
'(' =>
return Olbr;
')' =>
return Orbr;
'{' =>
return Olcbr;
'}' =>
return Orcbr;
'~' =>
return Ocom;
'.' =>
ungetc(c);
return lexnum();
',' =>
return Ocomma;
'?' =>
return Oquest;
':' =>
return follow('=', Odas, Ocolon);
';' =>
return Oscolon;
'↑' =>
return Onand;
'↓' =>
return Onor;
'∞' =>
lexval = Infinity;
return Onum;
* =>
error(nil, sys->sprint("bad character %c", c));
}
}
}
unlex(t: int)
{
lexes--;
buft = t;
if(t == Onl)
lineno--;
}
mustbe(t: int)
{
nt := lex();
if(nt != t)
error(nil, sys->sprint("expected %s not %s", opstring(t), opstring(nt)));
}
consume(t: int)
{
nt := lex();
if(nt != t)
unlex(nt);
}
elex(): int
{
t := lex();
if(binary(t))
return t;
if(hexp(t)){
unlex(t);
return Oscomma;
}
return t;
}
hexp(o: int): int
{
return preunary(o) || o == Olbr || atom(o);
}
atom(o: int): int
{
return o >= Ostring && o <= Olfun;
}
asop(o: int): int
{
return o == Oas || o == Odas || o >= Oadde && o <= Orshe || o >= Oinc && o <= Opostdec;
}
preunary(o: int): int
{
return ops[o]&Preunary;
}
postunary(o: int): int
{
return ops[o]&Postunary;
}
binary(o: int): int
{
return ops[o]&Binary;
}
prec(o: int): int
{
return ops[o]&Prec;
}
assoc(o: int): int
{
return ops[o]&Assoc;
}
rassoc(o: int): int
{
return ops[o]&Rassoc;
}
preop(o: int): int
{
case(o){
Oadd => return Oplus;
Osub => return Ominus;
Oinc => return Opreinc;
Odec => return Opredec;
Oexc => return Onot;
}
return o;
}
postop(o: int): int
{
case(o){
Oinc => return Opostinc;
Odec => return Opostdec;
Oexc => return Ofact;
}
return o;
}
prtree(p: ref Node, in: int)
{
if(p == nil)
return;
for(i := 0; i < in; i++)
sys->print(" ");
sys->print("%s ", opstring(p.op));
case(p.op){
Ostring =>
sys->print("%s", p.str);
Onum =>
sys->print("%g", p.val);
Ocon or
Ovar =>
sys->print("%s(%g)", p.dec.sym.name, p.dec.val);
Ofun or
Olfun =>
sys->print("%s", p.dec.sym.name);
}
sys->print("\n");
# sys->print(" - %d\n", p.src);
prtree(p.left, in+1);
prtree(p.right, in+1);
}
tree(o: int, l: ref Node, r: ref Node): ref Node
{
p := ref Node;
p.op = o;
p.left = l;
p.right = r;
p.src = lineno;
if(asop(o)){
if(o >= Oadde && o <= Orshe){
p = tree(Oas, l, p);
p.right.op += Oadd-Oadde;
}
}
return p;
}
itree(n: int): ref Node
{
return vtree(real n);
}
vtree(v: real): ref Node
{
n := tree(Onum, nil, nil);
n.val = v;
return n;
}
ltree(s: string, a: ref Node): ref Node
{
n := tree(Olfun, a, nil);
n.dec = lookup(s).dec;
return n;
}
ptree(n: ref Node, p: real): ref Node
{
if(isinteger(p)){
i := int p;
if(i == 0)
return itree(1);
if(i == 1)
return n;
if(i == -1)
return tree(Oinv, n, nil);
if(i < 0)
return tree(Oinv, tree(Oexp, n, itree(-i)), nil);
}
return tree(Oexp, n, vtree(p));
}
iscon(n: ref Node): int
{
return n.op == Onum || n.op == Ocon;
}
iszero(n: ref Node): int
{
return iscon(n) && eval(n) == 0.0;
}
isone(n: ref Node): int
{
return iscon(n) && eval(n) == 1.0;
}
isnan(n: ref Node): int
{
return iscon(n) && maths->isnan(eval(n));
}
isinf(n: ref Node): int
{
return iscon(n) && (v := eval(n)) == Infinity || v == -Infinity;
}
stat(scope: int): ref Node
{
e1, e2, e3, e4: ref Node;
consume(Onl);
t := lex();
case(t){
Olcbr =>
if(scope)
pushscope();
for(;;){
e2 = stat(1);
if(e1 == nil)
e1 = e2;
else
e1 = tree(Ocomma, e1, e2);
consume(Onl);
t = lex();
if(t == Oeof)
raise Eeof;
if(t == Orcbr)
break;
unlex(t);
}
if(scope)
popscope();
return e1;
Oprint or
Oread or
Oret =>
if(t == Oret && !infn)
error(nil, "return not in fn");
e1= tree(t, expr(0, 1), nil);
consume(Oscolon);
if(t == Oread)
allvar(e1.left);
return e1;
Oif =>
# mustbe(Olbr);
e1 = expr(0, 1);
# mustbe(Orbr);
e2 = stat(1);
e3 = nil;
consume(Onl);
t = lex();
if(t == Oelse)
e3 = stat(1);
else
unlex(t);
return tree(Oif, e1, tree(Ocomma, e2, e3));
Ofor =>
inloop++;
mustbe(Olbr);
e1 = expr(0, 1);
mustbe(Oscolon);
e2 = expr(0, 1);
mustbe(Oscolon);
e3 = expr(0, 1);
mustbe(Orbr);
e4 = stat(1);
inloop--;
return tree(Ocomma, e1, tree(Ofor, e2, tree(Ocomma, e4, e3)));
Owhile =>
inloop++;
# mustbe(Olbr);
e1 = expr(0, 1);
# mustbe(Orbr);
e2 = stat(1);
inloop--;
return tree(Ofor, e1, tree(Ocomma, e2, nil));
Odo =>
inloop++;
e1 = stat(1);
consume(Onl);
mustbe(Owhile);
# mustbe(Olbr);
e2 = expr(0, 1);
# mustbe(Orbr);
consume(Oscolon);
inloop--;
return tree(Odo, e1, e2);
Obreak or
Ocont or
Oexit =>
if((t == Obreak || t == Ocont) && !inloop)
error(nil, "break/continue not in loop");
consume(Oscolon);
return tree(t, nil, nil);
Ofn =>
if(infn)
error(nil, "nested functions not allowed");
infn++;
mustbe(Oident);
s := lexsym;
d := mkdec(s, Ofun, 1);
d.code = tree(Ofn, nil, nil);
pushscope();
(d.na, d.code.left) = args(0);
allvar(d.code.left);
pushparams(d.code.left);
d.code.right = stat(0);
popscope();
infn--;
return d.code;
Oinclude =>
e1 = expr(0, 0);
if(e1.op != Ostring)
error(nil, "bad include file");
consume(Oscolon);
doinclude(e1.str);
return nil;
* =>
unlex(t);
e1 = expr(0, 1);
consume(Oscolon);
if(debug)
prnode(e1);
return e1;
}
return nil;
}
ckstat(n: ref Node, parop: int, pr: int)
{
if(n == nil)
return;
pr |= n.op == Oprint;
ckstat(n.left, n.op, pr);
ckstat(n.right, n.op, pr);
case(n.op){
Ostring =>
if(!pr || parop != Oprint && parop != Ocomma)
error(n, "illegal string operation");
}
}
pexp(e: ref Node): int
{
if(e == nil)
return 0;
if(e.op == Ocomma)
return pexp(e.right);
return e.op >= Ostring && e.op <= Oiff && !asop(e.op);
}
expr(p: int, zok: int): ref Node
{
n := exp(p, zok);
ckexp(n, Onothing);
return n;
}
exp(p: int, zok: int): ref Node
{
l := prim(zok);
if(l == nil)
return nil;
while(binary(t := elex()) && (o := prec(t)) >= p){
if(rassoc(t))
r := exp(o, 0);
else
r = exp(o+1, 0);
if(t == Oscomma)
t = Ocomma;
l = tree(t, l, r);
}
if(t != Oscomma)
unlex(t);
return l;
}
prim(zok: int): ref Node
{
p: ref Node;
na: int;
t := lex();
if(preunary(t)){
t = preop(t);
return tree(t, exp(prec(t), 0), nil);
}
case(t){
Olbr =>
p = exp(0, zok);
mustbe(Orbr);
Ostring =>
p = tree(t, nil, nil);
p.str = lexstr;
Onum =>
p = tree(t, nil ,nil);
p.val = lexval;
Oident =>
s := lexsym;
d := s.dec;
if(d == nil)
d = mkdec(s, Ovar, 0);
case(t = d.kind){
Ocon or
Ovar =>
p = tree(t, nil, nil);
p.dec = d;
Ofun or
Olfun =>
p = tree(t, nil, nil);
p.dec = d;
(na, p.left) = args(prec(t));
if(!(t == Olfun && d.val == real Osolve && na == 2))
if(na != d.na)
error(p, "wrong number of arguments");
if(t == Olfun){
case(int d.val){
Osigma or
Opi or
Ocfrac or
Ointeg =>
if((op := p.left.left.left.op) != Oas && op != Odas)
error(p.left, "expression not an assignment");
Oderiv =>
if((op := p.left.left.op) != Oas && op != Odas)
error(p.left, "expression not an assignment");
}
}
}
* =>
unlex(t);
if(!zok)
error(nil, "missing expression");
return nil;
}
while(postunary(t = lex())){
t = postop(t);
p = tree(t, p, nil);
}
unlex(t);
return p;
}
ckexp(n: ref Node, parop: int)
{
if(n == nil)
return;
o := n.op;
l := n.left;
r := n.right;
if(asop(o))
var(l);
case(o){
Ovar =>
s := n.dec.sym;
d := s.dec;
if(d == nil){
if(strict)
error(n, sys->sprint("%s undefined", s.name));
d = mkdec(s, Ovar, 1);
}
n.dec = d;
Odas =>
ckexp(r, o);
l.dec = mkdec(l.dec.sym, Ovar, 1);
* =>
ckexp(l, o);
ckexp(r, o);
if(o == Oquest && r.op != Ocolon)
error(n, "bad '?' operator");
if(o == Ocolon && parop != Oquest)
error(n, "bad ':' operator");
}
}
commas(n: ref Node): int
{
if(n == nil || n.op == Ofun || n.op == Olfun)
return 0;
c := commas(n.left)+commas(n.right);
if(n.op == Ocomma)
c++;
return c;
}
allvar(n: ref Node)
{
if(n == nil)
return;
if(n.op == Ocomma){
allvar(n.left);
allvar(n.right);
return;
}
var(n);
}
args(p: int): (int, ref Node)
{
if(!p)
mustbe(Olbr);
a := exp(p, 1);
if(!p)
mustbe(Orbr);
na := 0;
if(a != nil)
na = commas(a)+1;
return (na, a);
}
hash(s: string): int
{
l := len s;
h := 4104;
for(i := 0; i < l; i++)
h = 1729*h ^ s[i];
if(h < 0)
h = -h;
return h&(Hash-1);
}
enter(sp: string, k: int): ref Sym
{
for(s := syms[hash(sp)]; s != nil; s = s.next){
if(sp == s.name)
return s;
}
s = ref Sym;
s.name = sp;
s.kind = k;
h := hash(sp);
s.next = syms[h];
syms[h] = s;
return s;
}
lookup(sp: string): ref Sym
{
return enter(sp, Oident);
}
mkdec(s: ref Sym, k: int, dec: int): ref Dec
{
d := ref Dec;
d.kind = k;
d.val = 0.0;
d.na = 0;
d.sym = s;
d.scope = 0;
if(dec)
pushdec(d);
return d;
}
adddec(sp: string, k: int, v: real, n: int): ref Dec
{
d := mkdec(enter(sp, Oident), k, 1);
d.val = v;
d.na = n;
return d;
}
scope: int;
curscope: ref Dec;
scopes: list of ref Dec;
pushscope()
{
scope++;
scopes = curscope :: scopes;
curscope = nil;
}
popscope()
{
popdecs();
curscope = hd scopes;
scopes = tl scopes;
scope--;
}
pushparams(n: ref Node)
{
if(n == nil)
return;
if(n.op == Ocomma){
pushparams(n.left);
pushparams(n.right);
return;
}
n.dec = mkdec(n.dec.sym, Ovar, 1);
}
pushdec(d: ref Dec)
{
if(0 && debug)
sys->print("dec %s scope %d\n", d.sym.name, scope);
d.scope = scope;
s := d.sym;
if(s.dec != nil && s.dec.scope == scope)
error(nil, sys->sprint("redeclaration of %s", s.name));
d.old = s.dec;
s.dec = d;
d.next = curscope;
curscope = d;
}
popdecs()
{
nd: ref Dec;
for(d := curscope; d != nil; d = nd){
d.sym.dec = d.old;
d.old = nil;
nd = d.next;
d.next = nil;
}
curscope = nil;
}
estat(n: ref Node): (int, real)
{
k: int;
v: real;
if(n == nil)
return (Onothing, 0.0);
l := n.left;
r := n.right;
case(n.op){
Ocomma =>
(k, v) = estat(l);
if(k == Oexit || k == Oret || k == Obreak || k == Ocont)
return (k, v);
return estat(r);
Oprint =>
v = print(l);
return (Onothing, v);
Oread =>
v = read(l);
return (Onothing, v);
Obreak or
Ocont or
Oexit =>
return (n.op, 0.0);
Oret =>
return (Oret, eval(l));
Oif =>
v = eval(l);
if(int v)
return estat(r.left);
else if(r.right != nil)
return estat(r.right);
else
return (Onothing, v);
Ofor =>
for(;;){
v = eval(l);
if(!int v)
break;
(k, v) = estat(r.left);
if(k == Oexit || k == Oret)
return (k, v);
if(k == Obreak)
break;
if(r.right != nil)
v = eval(r.right);
}
return (Onothing, v);
Odo =>
for(;;){
(k, v) = estat(l);
if(k == Oexit || k == Oret)
return (k, v);
if(k == Obreak)
break;
v = eval(r);
if(!int v)
break;
}
return (Onothing, v);
* =>
return (Onothing, eval(n));
}
return (Onothing, 0.0);
}
eval(e: ref Node): real
{
lv, rv: real;
if(e == nil)
return 1.0;
o := e.op;
l := e.left;
r := e.right;
if(o != Ofun && o != Olfun)
lv = eval(l);
if(o != Oandand && o != Ooror && o != Oquest)
rv = eval(r);
case(o){
Ostring =>
return 0.0;
Onum =>
return e.val;
Ocon or
Ovar =>
return e.dec.val;
Ofun =>
return call(e.dec, l);
Olfun =>
return libfun(int e.dec.val, l);
Oadd =>
return lv+rv;
Osub =>
return lv-rv;
Omul =>
return lv*rv;
Odiv =>
return lv/rv;
Omod =>
return real (big lv%big rv);
Oidiv =>
return real (big lv/big rv);
Oand =>
return real (big lv&big rv);
Oor =>
return real (big lv|big rv);
Oxor =>
return real (big lv^big rv);
Olsh =>
return real (big lv<<int rv);
Orsh =>
return real (big lv>>int rv);
Oeq =>
return real (lv == rv);
One =>
return real (lv != rv);
Ogt =>
return real (lv > rv);
Olt =>
return real (lv < rv);
Oge =>
return real (lv >= rv);
Ole =>
return real (lv <= rv);
Opreinc =>
l.dec.val += 1.0;
return l.dec.val;
Opostinc =>
l.dec.val += 1.0;
return l.dec.val-1.0;
Opredec =>
l.dec.val -= 1.0;
return l.dec.val;
Opostdec =>
l.dec.val -= 1.0;
return l.dec.val+1.0;
Oexp =>
if(isinteger(rv) && rv >= 0.0)
return lv**int rv;
return maths->pow(lv, rv);
Oandand =>
if(!int lv)
return lv;
return eval(r);
Ooror =>
if(int lv)
return lv;
return eval(r);
Onot =>
return real !int lv;
Ofact =>
if(isinteger(lv) && lv >= 0.0){
n := int lv;
lv = 1.0;
for(i := 2; i <= n; i++)
lv *= real i;
return lv;
}
return gamma(lv+1.0);
Ocom =>
return real ~big lv;
Oas or
Odas =>
l.dec.val = rv;
return rv;
Oplus =>
return lv;
Ominus =>
return -lv;
Oinv =>
return 1.0/lv;
Ocomma =>
return rv;
Oquest =>
if(int lv)
return eval(r.left);
else
return eval(r.right);
Onand =>
return real !(int lv&int rv);
Onor =>
return real !(int lv|int rv);
Oimp =>
return real (!int lv|int rv);
Oimpby =>
return real (int lv|!int rv);
Oiff =>
return real !(int lv^int rv);
* =>
fatal(sys->sprint("case %s in eval", opstring(o)));
}
return 0.0;
}
var(e: ref Node)
{
if(e == nil || e.op != Ovar || e.dec.kind != Ovar)
error(e, "expected a variable");
}
libfun(o: int, a: ref Node): real
{
a1, a2: real;
case(o){
Osolve =>
return solve(a);
Osigma or
Opi or
Ocfrac =>
return series(o, a);
Oderiv =>
return differential(a);
Ointeg =>
return integral(a);
}
v := 0.0;
if(a != nil && a.op == Ocomma){
a1 = eval(a.left);
a2 = eval(a.right);
}
else
a1 = eval(a);
case(o){
Olog =>
v = maths->log(a1);
Olog10 =>
v = maths->log10(a1);
Olog2 =>
v = maths->log(a1)/maths->log(2.0);
Ologb =>
v = maths->log(a1)/maths->log(a2);
Oexpf =>
v = maths->exp(a1);
Opow =>
v = maths->pow(a1, a2);
Osqrt =>
v = maths->sqrt(a1);
Ocbrt =>
v = maths->cbrt(a1);
Ofloor =>
v = maths->floor(a1);
Oceil =>
v = maths->ceil(a1);
Omin =>
v = maths->fmin(a1, a2);
Omax =>
v = maths->fmax(a1, a2);
Oabs =>
v = maths->fabs(a1);
Ogamma =>
v = gamma(a1);
Osign =>
if(a1 > 0.0)
v = 1.0;
else if(a1 < 0.0)
v = -1.0;
else
v = 0.0;
Oint =>
(vi, nil) := maths->modf(a1);
v = real vi;
Ofrac =>
(nil, v) = maths->modf(a1);
Oround =>
v = maths->rint(a1);
Oerf =>
v = maths->erf(a1);
Osin =>
v = maths->sin(D2R(a1));
Ocos =>
v = maths->cos(D2R(a1));
Otan =>
v = maths->tan(D2R(a1));
Oasin =>
v = R2D(maths->asin(a1));
Oacos =>
v = R2D(maths->acos(a1));
Oatan =>
v = R2D(maths->atan(a1));
Oatan2 =>
v = R2D(maths->atan2(a1, a2));
Osinh =>
v = maths->sinh(a1);
Ocosh =>
v = maths->cosh(a1);
Otanh =>
v = maths->tanh(a1);
Oasinh =>
v = maths->asinh(a1);
Oacosh =>
v = maths->acosh(a1);
Oatanh =>
v = maths->atanh(a1);
Orand =>
v = real rand->rand(Big)/real Big;
* =>
fatal(sys->sprint("case %s in libfun", opstring(o)));
}
return v;
}
series(o: int, a: ref Node): real
{
p0, p1, q0, q1: real;
l := a.left;
r := a.right;
if(o == Osigma)
v := 0.0;
else if(o == Opi)
v = 1.0;
else{
p0 = q1 = 0.0;
p1 = q0 = 1.0;
v = Infinity;
}
i := l.left.left.dec;
ov := i.val;
i.val = eval(l.left.right);
eq := 0;
for(;;){
rv := eval(l.right);
if(i.val > rv)
break;
lv := v;
ev := eval(r);
if(o == Osigma)
v += ev;
else if(o == Opi)
v *= ev;
else{
t := ev*p1+p0;
p0 = p1;
p1 = t;
t = ev*q1+q0;
q0 = q1;
q1 = t;
v = p1/q1;
}
if(v == lv && rv == Infinity){
eq++;
if(eq > 100)
break;
}
else
eq = 0;
i.val += 1.0;
}
i.val = ov;
return v;
}
pushe(a: ref Node, l: list of real): list of real
{
if(a == nil)
return l;
if(a.op == Ocomma){
l = pushe(a.left, l);
return pushe(a.right, l);
}
l = eval(a) :: l;
return l;
}
pusha(f: ref Node, l: list of real, nl: list of real): (list of real, list of real)
{
if(f == nil)
return (l, nl);
if(f.op == Ocomma){
(l, nl) = pusha(f.left, l, nl);
return pusha(f.right, l, nl);
}
l = f.dec.val :: l;
f.dec.val = hd nl;
return (l, tl nl);
}
pop(f: ref Node, l: list of real): list of real
{
if(f == nil)
return l;
if(f.op == Ocomma){
l = pop(f.left, l);
return pop(f.right, l);
}
f.dec.val = hd l;
return tl l;
}
rev(l: list of real): list of real
{
nl: list of real;
for( ; l != nil; l = tl l)
nl = hd l :: nl;
return nl;
}
call(d: ref Dec, a: ref Node): real
{
l: list of real;
nl := rev(pushe(a, nil));
(l, nil) = pusha(d.code.left, nil, nl);
l = rev(l);
(k, v) := estat(d.code.right);
l = pop(d.code.left, l);
if(k == Oexit)
exit;
return v;
}
print(n: ref Node): real
{
if(n == nil)
return 0.0;
if(n.op == Ocomma){
print(n.left);
return print(n.right);
}
if(n.op == Ostring){
sys->print("%s", n.str);
return 0.0;
}
v := eval(n);
printnum(v, "");
return v;
}
read(n: ref Node): real
{
bio: ref Iobuf;
if(n == nil)
return 0.0;
if(n.op == Ocomma){
read(n.left);
return read(n.right);
}
sys->print("%s ? ", n.dec.sym.name);
if(!stdin){
bio = bufio->fopen(sys->fildes(0), Sys->OREAD);
stack(nil, bio);
}
lexnum();
consume(Onl);
n.dec.val = lexval;
if(!stdin && bin == bio)
unstack();
return n.dec.val;
}
isint(v: real): int
{
return v >= -real Maxint && v <= real Maxint;
}
isinteger(v: real): int
{
return v == real int v && isint(v);
}
split(v: real): (int, real)
{
# v >= 0.0
n := int v;
if(real n > v)
n--;
return (n, v-real n);
}
n2c(n: int): int
{
if(n < 10)
return n+'0';
return n-10+'a';
}
gamma(v: real): real
{
(s, lg) := maths->lgamma(v);
return real s*maths->exp(lg);
}
D2R(a: real): real
{
if(deg.val != 0.0)
a *= Pi/180.0;
return a;
}
R2D(a: real): real
{
if(deg.val != 0.0)
a /= Pi/180.0;
return a;
}
side(n: ref Node): int
{
if(n == nil)
return 0;
if(asop(n.op) || n.op == Ofun)
return 1;
return side(n.left) || side(n.right);
}
sametree(n1: ref Node, n2: ref Node): int
{
if(n1 == n2)
return 1;
if(n1 == nil || n2 == nil)
return 0;
if(n1.op != n2.op)
return 0;
case(n1.op){
Ostring =>
return n1.str == n2.str;
Onum =>
return n1.val == n2.val;
Ocon or
Ovar =>
return n1.dec == n2.dec;
Ofun or
Olfun =>
return n1.dec == n2.dec && sametree(n1.left, n2.left);
* =>
return sametree(n1.left, n2.left) && sametree(n1.right, n2.right);
}
return 0;
}
simplify(n: ref Node): ref Node
{
if(n == nil)
return nil;
op := n.op;
l := n.left = simplify(n.left);
r := n.right = simplify(n.right);
if(l != nil && iscon(l) && (r == nil || iscon(r))){
if(isnan(l))
return l;
if(r != nil && isnan(r))
return r;
return vtree(eval(n));
}
case(op){
Onum or
Ocon or
Ovar or
Olfun or
Ocomma =>
return n;
Oplus =>
return l;
Ominus =>
if(l.op == Ominus)
return l.left;
Oinv =>
if(l.op == Oinv)
return l.left;
Oadd =>
if(iszero(l))
return r;
if(iszero(r))
return l;
if(sametree(l, r))
return tree(Omul, itree(2), l);
Osub =>
if(iszero(l))
return simplify(tree(Ominus, r, nil));
if(iszero(r))
return l;
if(sametree(l, r))
return itree(0);
Omul =>
if(iszero(l))
return l;
if(iszero(r))
return r;
if(isone(l))
return r;
if(isone(r))
return l;
if(sametree(l, r))
return tree(Oexp, l, itree(2));
Odiv =>
if(iszero(l))
return l;
if(iszero(r))
return vtree(Infinity);
if(isone(l))
return ptree(r, -1.0);
if(isone(r))
return l;
if(sametree(l, r))
return itree(1);
Oexp =>
if(iszero(l))
return l;
if(iszero(r))
return itree(1);
if(isone(l))
return l;
if(isone(r))
return l;
* =>
fatal(sys->sprint("case %s in simplify", opstring(op)));
}
return n;
}
deriv(n: ref Node, d: ref Dec): ref Node
{
if(n == nil)
return nil;
op := n.op;
l := n.left;
r := n.right;
case(op){
Onum or
Ocon =>
n = itree(0);
Ovar =>
if(d == n.dec)
n = itree(1);
else
n = itree(0);
Olfun =>
case(int n.dec.val){
Olog =>
n = ptree(l, -1.0);
Olog10 =>
n = ptree(tree(Omul, l, vtree(Ln10)), -1.0);
Olog2 =>
n = ptree(tree(Omul, l, vtree(Ln2)), -1.0);
Oexpf =>
n = n;
Opow =>
return deriv(tree(Oexp, l.left, l.right), d);
Osqrt =>
return deriv(tree(Oexp, l, vtree(0.5)), d);
Ocbrt =>
return deriv(tree(Oexp, l, vtree(1.0/3.0)), d);
Osin =>
n = ltree("cos", l);
Ocos =>
n = tree(Ominus, ltree("sin", l), nil);
Otan =>
n = ptree(ltree("cos", l), -2.0);
Oasin =>
n = ptree(tree(Osub, itree(1), ptree(l, 2.0)), -0.5);
Oacos =>
n = tree(Ominus, ptree(tree(Osub, itree(1), ptree(l, 2.0)), -0.5), nil);
Oatan =>
n = ptree(tree(Oadd, itree(1), ptree(l, 2.0)), -1.0);
Osinh =>
n = ltree("cosh", l);
Ocosh =>
n = ltree("sinh", l);
Otanh =>
n = ptree(ltree("cosh", l), -2.0);
Oasinh =>
n = ptree(tree(Oadd, itree(1), ptree(l, 2.0)), -0.5);
Oacosh =>
n = ptree(tree(Osub, ptree(l, 2.0), itree(1)), -0.5);
Oatanh =>
n = ptree(tree(Osub, itree(1), ptree(l, 2.0)), -1.0);
* =>
return vtree(Nan);
}
return tree(Omul, n, deriv(l, d));
Oplus or
Ominus =>
n = tree(op, deriv(l, d), nil);
Oinv =>
n = tree(Omul, tree(Ominus, ptree(l, -2.0), nil), deriv(l, d));
Oadd or
Osub or
Ocomma =>
n = tree(op, deriv(l, d), deriv(r, d));
Omul =>
n = tree(Oadd, tree(Omul, deriv(l, d), r), tree(Omul, l, deriv(r, d)));
Odiv =>
n = tree(Osub, tree(Omul, deriv(l, d), r), tree(Omul, l, deriv(r, d)));
n = tree(Odiv, n, ptree(r, 2.0));
Oexp =>
nn := tree(Oadd, tree(Omul, deriv(l, d), tree(Odiv, r, l)), tree(Omul, ltree("log", l), deriv(r, d)));
n = tree(Omul, n, nn);
* =>
n = vtree(Nan);
}
return n;
}
derivative(n: ref Node, d: ref Dec): ref Node
{
n = simplify(deriv(n, d));
if(isnan(n))
error(n, "no derivative");
if(debug)
prnode(n);
return n;
}
newton(f: ref Node, e: ref Node, d: ref Dec, v1: real, v2: real): (int, real)
{
v := (v1+v2)/2.0;
lv := 0.0;
its := 0;
for(;;){
lv = v;
d.val = v;
v = eval(e);
# if(v < v1 || v > v2)
# return (0, 0.0);
if(maths->isnan(v))
return (0, 0.0);
if(its > 100 || fabs(v-lv) < Eps)
break;
its++;
}
if(fabs(v-lv) > Bigeps || fabs(eval(f)) > Bigeps)
return (0, 0.0);
return (1, v);
}
solve(n: ref Node): real
{
d: ref Dec;
if(n == nil)
return Nan;
if(n.op == Ocomma){ # solve(..., var)
var(n.right);
d = n.right.dec;
n = n.left;
if(!varmem(n, d))
error(n, "variable not in equation");
}
else{
d = findvar(n, nil);
if(d == nil)
error(n, "variable missing");
if(d == errdec)
error(n, "one variable only required");
}
if(n.op == Oeq)
n.op = Osub;
dn := derivative(n, d);
var := tree(Ovar, nil, nil);
var.dec = d;
nr := tree(Osub, var, tree(Odiv, n, dn));
ov := d.val;
lim := lookup(Limit).dec.val;
step := lookup(Step).dec.val;
rval := Infinity;
d.val = -lim-step;
v1 := 0.0;
v2 := eval(n);
for(v := -lim; v <= lim; v += step){
d.val = v;
v1 = v2;
v2 = eval(n);
if(maths->isnan(v2)) # v == nan, v <= nan, v >= nan all give 1
continue;
if(fabs(v2) < Eps){
if(v >= -lim && v <= lim && v != rval){
printnum(v, " ");
rval = v;
}
}
else if(v1*v2 <= 0.0){
(f, rv) := newton(n, nr, var.dec, v-step, v);
if(f && rv >= -lim && rv <= lim && rv != rval){
printnum(rv, " ");
rval = rv;
}
}
}
d.val = ov;
if(rval == Infinity)
error(n, "no roots found");
else
sys->print("\n");
return rval;
}
differential(n: ref Node): real
{
x := n.left.left.dec;
ov := x.val;
v := evalx(derivative(n.right, x), x, eval(n.left.right));
x.val = ov;
return v;
}
integral(n: ref Node): real
{
l := n.left;
r := n.right;
x := l.left.left.dec;
ov := x.val;
a := eval(l.left.right);
b := eval(l.right);
h := b-a;
end := evalx(r, x, a) + evalx(r, x, b);
odd := even := 0.0;
oldarea := 0.0;
area := h*end/2.0;
for(i := 1; i < 1<<16; i <<= 1){
even += odd;
odd = 0.0;
xv := a+h/2.0;
for(j := 0; j < i; j++){
odd += evalx(r, x, xv);
xv += h;
}
h /= 2.0;
oldarea = area;
area = h*(end+4.0*odd+2.0*even)/3.0;
if(maths->isnan(area))
error(n, "integral not found");
if(fabs(area-oldarea) < Eps)
break;
}
if(fabs(area-oldarea) > Bigeps)
error(n, "integral not found");
x.val = ov;
return area;
}
evalx(n: ref Node, d: ref Dec, v: real): real
{
d.val = v;
return eval(n);
}
findvar(n: ref Node, d: ref Dec): ref Dec
{
if(n == nil)
return d;
d = findvar(n.left, d);
d = findvar(n.right, d);
if(n.op == Ovar){
if(d == nil)
d = n.dec;
if(n.dec != d)
d = errdec;
}
return d;
}
varmem(n: ref Node, d: ref Dec): int
{
if(n == nil)
return 0;
if(n.op == Ovar)
return d == n.dec;
return varmem(n.left, d) || varmem(n.right, d);
}
fabs(r: real): real
{
if(r < 0.0)
return -r;
return r;
}
cvt(v: real, base: int): string
{
if(base == 10)
return sys->sprint("%g", v);
neg := 0;
if(v < 0.0){
neg = 1;
v = -v;
}
if(!isint(v)){
n := 0;
lg := maths->log(v)/maths->log(real base);
if(lg < 0.0){
(n, nil) = split(-lg);
v *= real base**n;
n = -n;
}
else{
(n, nil) = split(lg);
v /= real base**n;
}
s := cvt(v, base) + "E" + string n;
if(neg)
s = "-" + s;
return s;
}
(n, f) := split(v);
s := "";
do{
r := n%base;
n /= base;
s[len s] = n2c(r);
}while(n != 0);
ls := len s;
for(i := 0; i < ls/2; i++){
t := s[i];
s[i] = s[ls-1-i];
s[ls-1-i] = t;
}
if(f != 0.0){
s[len s] = '.';
for(i = 0; i < 16 && f != 0.0; i++){
f *= real base;
(n, f) = split(f);
s[len s] = n2c(n);
}
}
s = string base + "r" + s;
if(neg)
s = "-" + s;
return s;
}
printnum(v: real, s: string)
{
base := int pbase.val;
if(!isinteger(pbase.val) || base < 2 || base > 36)
base = 10;
sys->print("%s%s", cvt(v, base), s);
if(bits){
r := array[1] of real;
b := array[8] of byte;
r[0] = v;
maths->export_real(b, r);
for(i := 0; i < 8; i++)
sys->print("%2.2x ", int b[i]);
sys->print("\n");
}
}
Left, Right, Pre, Post: con 1<<iota;
lspace := array[] of { 0, 0, 2, 3, 4, 5, 0, 0, 0, 9, 10, 0, 0, 0, 0, 0, 0, 0 };
rspace := array[] of { 0, 1, 2, 3, 4, 5, 0, 0, 0, 9, 10, 0, 0, 0, 0, 0, 0, 0 };
preced(op1: int, op2: int, s: int): int
{
br := 0;
p1 := prec(op1);
p2 := prec(op2);
if(p1 > p2)
br = 1;
else if(p1 == p2){
if(op1 == op2){
if(rassoc(op1))
br = s == Left;
else
br = s == Right && !assoc(op1);
}
else{
if(rassoc(op1))
br = s == Left;
else
br = s == Right && op1 != Oadd;
if(postunary(op1) && preunary(op2))
br = 1;
}
}
return br;
}
prnode(n: ref Node)
{
pnode(n, Onothing, Pre);
sys->print("\n");
}
pnode(n: ref Node, opp: int, s: int)
{
if(n == nil)
return;
op := n.op;
if(br := preced(opp, op, s))
sys->print("(");
if(op == Oas && n.right.op >= Oadd && n.right.op <= Orsh && n.left == n.right.left){
pnode(n.left, op, Left);
sys->print(" %s ", opstring(n.right.op+Oadde-Oadd));
pnode(n.right.right, op, Right);
}
else if(binary(op)){
p := prec(op);
pnode(n.left, op, Left);
if(lspace[p])
sys->print(" ");
sys->print("%s", opstring(op));
if(rspace[p])
sys->print(" ");
pnode(n.right, op, Right);
}
else if(op == Oinv){ # cannot print postunary -1
sys->print("%s", opstring(op));
pnode(n.left, Odiv, Right);
}
else if(preunary(op)){
sys->print("%s", opstring(op));
pnode(n.left, op, Pre);
}
else if(postunary(op)){
pnode(n.left, op, Post);
sys->print("%s", opstring(op));
}
else{
case(op){
Ostring =>
sys->print("%s", n.str);
Onum =>
sys->print("%g", n.val);
Ocon or
Ovar =>
sys->print("%s", n.dec.sym.name);
Ofun or
Olfun =>
sys->print("%s(", n.dec.sym.name);
pnode(n.left, Onothing, Pre);
sys->print(")");
* =>
fatal(sys->sprint("bad op %s in pnode()", opstring(op)));
}
}
if(br)
sys->print(")");
}