summaryrefslogtreecommitdiff
path: root/appl/cmd/disk/prep/calc.y
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/cmd/disk/prep/calc.y
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/cmd/disk/prep/calc.y')
-rw-r--r--appl/cmd/disk/prep/calc.y174
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);
+ }
+}
+