diff options
Diffstat (limited to 'appl/cmd/disk/prep/calc.y')
| -rw-r--r-- | appl/cmd/disk/prep/calc.y | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/appl/cmd/disk/prep/calc.y b/appl/cmd/disk/prep/calc.y new file mode 100644 index 00000000..7ce56049 --- /dev/null +++ b/appl/cmd/disk/prep/calc.y @@ -0,0 +1,174 @@ +%{ +# +# 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); + } +} + |
