ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/cmd/disk/prep/calc.y/
%{
#
# from Plan 9. subject to the Lucent Public License 1.02
#
include "sys.m";
sys: Sys;
include "draw.m";
NUM,
DOT,
DOLLAR,
ADD,
SUB,
MUL,
DIV,
FRAC,
NEG: con iota;
Exp: adt {
ty: int;
n: big;
e1, e2: cyclic ref Exp;
};
YYSTYPE: adt {
e: ref Exp;
};
yyexp: ref Exp;
YYLEX: adt {
s: string;
n: int;
lval: YYSTYPE;
lex: fn(l: self ref YYLEX): int;
error: fn(l: self ref YYLEX, msg: string);
};
%}
%module Calc
{
parseexpr: fn(s: string, a, b, c: big): (big, string);
init: fn(nil: ref Draw->Context, nil: list of string);
}
%token <e> NUMBER
%type <e> expr
%left '+' '-'
%left '*' '/'
%left UNARYMINUS '%'
%%
top: expr { yyexp = $1; return 0; }
expr: NUMBER
| '.' { $$ = mkOP(DOT, nil, nil); }
| '$' { $$ = mkOP(DOLLAR, nil, nil); }
| '(' expr ')' { $$ = $2; }
| expr '+' expr { $$ = mkOP(ADD, $1, $3); }
| expr '-' expr { $$ = mkOP(SUB, $1, $3); }
| expr '*' expr { $$ = mkOP(MUL, $1, $3); }
| expr '/' expr { $$ = mkOP(DIV, $1, $3); }
| expr '%' { $$ = mkOP(FRAC, $1, nil); }
| '-' expr %prec UNARYMINUS { $$ = mkOP(NEG, $2, nil); }
;
%%
mkNUM(x: big): ref Exp
{
return ref Exp(NUM, x, nil, nil);
}
mkOP(ty: int, e1: ref Exp, e2: ref Exp): ref Exp
{
return ref Exp(ty, big 0, e1, e2);
}
dot, size, dollar: big;
YYLEX.lex(l: self ref YYLEX): int
{
while(l.n < len l.s && isspace(l.s[l.n]))
l.n++;
if(l.n == len l.s)
return -1;
if(isdigit(l.s[l.n])){
for(o := l.n; o < len l.s && isdigit(l.s[o]); o++)
;
l.lval.e = mkNUM(big l.s[l.n:o]);
l.n = o;
return NUMBER;
}
return l.s[l.n++];
}
isdigit(c: int): int
{
return c >= '0' && c <= '9';
}
isspace(c: int): int
{
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f';
}
YYLEX.error(nil: self ref YYLEX, s: string)
{
raise s;
}
eval(e: ref Exp): big
{
case e.ty {
NUM =>
return e.n;
DOT =>
return dot;
DOLLAR =>
return dollar;
ADD =>
return eval(e.e1)+eval(e.e2);
SUB =>
return eval(e.e1)-eval(e.e2);
MUL =>
return eval(e.e1)*eval(e.e2);
DIV =>
i := eval(e.e2);
if(i == big 0)
raise "division by zero";
return eval(e.e1)/i;
FRAC =>
return (size*eval(e.e1))/big 100;
NEG =>
return -eval(e.e1);
* =>
raise "invalid operator";
}
}
parseexpr(s: string, xdot: big, xdollar: big, xsize: big): (big, string)
{
dot = xdot;
size = xsize;
dollar = xdollar;
l := ref YYLEX(s, 0, YYSTYPE(nil));
{
yyparse(l);
if(yyexp == nil)
return (big 0, "nil yylval?");
return (eval(yyexp), nil);
}exception e{
"*" =>
return (big 0, e);
}
}
init(nil: ref Draw->Context, args: list of string)
{
sys = load Sys Sys->PATH;
while((args = tl args) != nil){
(r, e) := parseexpr(hd args, big 1000, big 1000000, big 1000000);
if(e != nil)
sys->print("%s\n", e);
else
sys->print("%bd\n", r);
}
}