From 37da2899f40661e3e9631e497da8dc59b971cbd0 Mon Sep 17 00:00:00 2001 From: "Charles.Forsyth" Date: Fri, 22 Dec 2006 17:07:39 +0000 Subject: 20060303a --- appl/cmd/units.y | 771 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 771 insertions(+) create mode 100644 appl/cmd/units.y (limited to 'appl/cmd/units.y') diff --git a/appl/cmd/units.y b/appl/cmd/units.y new file mode 100644 index 00000000..70284868 --- /dev/null +++ b/appl/cmd/units.y @@ -0,0 +1,771 @@ +%{ +# +# subject to the Lucent Public License 1.02 +# +include "sys.m"; + sys: Sys; + +include "draw.m"; + +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; + +include "math.m"; + math: Math; + +include "arg.m"; + +Ndim: con 15; # number of dimensions +Nvar: con 203; # hash table size +Maxe: con 695.0; # log of largest number + +Node: adt +{ + val: real; + dim: array of int; # [Ndim] schar + + mk: fn(v: real): Node; + text: fn(n: self Node): string; + add: fn(a: self Node, b: Node): Node; + sub: fn(a: self Node, b: Node): Node; + mul: fn(a: self Node, b: Node): Node; + div: fn(a: self Node, b: Node): Node; + xpn: fn(a: self Node, b: int): Node; + copy: fn(a: self Node): Node; +}; +Var: adt +{ + name: string; + node: Node; +}; +Prefix: adt +{ + val: real; + pname: string; +}; + +digval := 0; +fi: ref Iobuf; +fund := array[Ndim] of ref Var; +line: string; +lineno := 0; +linep := 0; +nerrors := 0; +peekrune := 0; +retnode1: Node; +retnode2: Node; +retnode: Node; +sym: string; +vars := array[Nvar] of list of ref Var; +vflag := 0; + +YYSTYPE: adt { + node: Node; + var: ref Var; + numb: int; + val: real; +}; + +YYLEX: adt { + lval: YYSTYPE; + lex: fn(l: self ref YYLEX): int; + error: fn(l: self ref YYLEX, msg: string); +}; + +%} +%module Units +{ + init: fn(nil: ref Draw->Context, args: list of string); +} + +%type prog expr expr0 expr1 expr2 expr3 expr4 + +%token VAL +%token VAR +%token SUP +%% +prog: + ':' VAR expr + { + f := $2.node.dim[0]; + $2.node = $3.copy(); + $2.node.dim[0] = 1; + if(f) + yyerror(sys->sprint("redefinition of %s", $2.name)); + else if(vflag) + sys->print("%s\t%s\n", $2.name, $2.node.text()); + } +| ':' VAR '#' + { + for(i:=1; i= Ndim) { + yyerror("too many dimensions"); + i = Ndim-1; + } + fund[i] = $2; + + f := $2.node.dim[0]; + $2.node = Node.mk(1.0); + $2.node.dim[0] = 1; + $2.node.dim[i] = 1; + if(f) + yyerror(sys->sprint("redefinition of %s", $2.name)); + else if(vflag) + sys->print("%s\t#\n", $2.name); + } +| '?' expr + { + retnode1 = $2.copy(); + } +| '?' + { + retnode1 = Node.mk(1.0); + } + +expr: + expr4 +| expr '+' expr4 + { + $$ = $1.add($3); + } +| expr '-' expr4 + { + $$ = $1.sub($3); + } + +expr4: + expr3 +| expr4 '*' expr3 + { + $$ = $1.mul($3); + } +| expr4 '/' expr3 + { + $$ = $1.div($3); + } + +expr3: + expr2 +| expr3 expr2 + { + $$ = $1.mul($2); + } + +expr2: + expr1 +| expr2 SUP + { + $$ = $1.xpn($2); + } +| expr2 '^' expr1 + { + for(i:=1; i= Ndim) { + i = int $3.val; + if(real i != $3.val) + yyerror("exponent not integral"); + $$ = $1.xpn(i); + } + } + +expr1: + expr0 +| expr1 '|' expr0 + { + $$ = $1.div($3); + } + +expr0: + VAR + { + if($1.node.dim[0] == 0) { + yyerror(sys->sprint("undefined %s", $1.name)); + $$ = Node.mk(1.0); + } else + $$ = $1.node.copy(); + } +| VAL + { + $$ = Node.mk($1); + } +| '(' expr ')' + { + $$ = $2; + } +%% + +init(nil: ref Draw->Context, args: list of string) +{ + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + math = load Math Math->PATH; + + arg := load Arg Arg->PATH; + arg->init(args); + arg->setusage("units [-v] [file]"); + while((o := arg->opt()) != 0) + case o { + 'v' => vflag = 1; + * => arg->usage(); + } + args = arg->argv(); + arg = nil; + + file := "/lib/units"; + if(args != nil) + file = hd args; + fi = bufio->open(file, Sys->OREAD); + if(fi == nil) { + sys->fprint(sys->fildes(2), "units: cannot open %s: %r\n", file); + raise "fail:open"; + } + lex := ref YYLEX; + + # + # read the 'units' file to + # develop a database + # + lineno = 0; + for(;;) { + lineno++; + if(readline()) + break; + if(len line == 0 || line[0] == '/') + continue; + peekrune = ':'; + yyparse(lex); + } + + # + # read the console to + # print ratio of pairs + # + fi = bufio->fopen(sys->fildes(0), Sys->OREAD); + lineno = 0; + for(;;) { + if(lineno & 1) + sys->print("you want: "); + else + sys->print("you have: "); + if(readline()) + break; + peekrune = '?'; + nerrors = 0; + yyparse(lex); + if(nerrors) + continue; + if(lineno & 1) { + isspcl: int; + (isspcl, retnode) = specialcase(retnode2, retnode1); + if(isspcl) + sys->print("\tis %s\n", retnode.text()); + else { + retnode = retnode2.div(retnode1); + sys->print("\t* %s\n", retnode.text()); + retnode = retnode1.div(retnode2); + sys->print("\t/ %s\n", retnode.text()); + } + } else + retnode2 = retnode1.copy(); + lineno++; + } + sys->print("\n"); +} + +YYLEX.lex(lex: self ref YYLEX): int +{ + c := peekrune; + peekrune = ' '; + + while(c == ' ' || c == '\t'){ + if(linep >= len line) + return 0; # -1? + c = line[linep++]; + } + case c { + '0' to '9' or '.' => + digval = c; + (lex.lval.val, peekrune) = readreal(gdigit, lex); + return VAL; + '×' => + return '*'; + '÷' => + return '/'; + '¹' or + 'ⁱ' => + lex.lval.numb = 1; + return SUP; + '²' or + '⁲' => + lex.lval.numb = 2; + return SUP; + '³' or + '⁳' => + lex.lval.numb = 3; + return SUP; + * => + if(ralpha(c)){ + sym = ""; + for(i:=0;; i++) { + sym[i] = c; + if(linep >= len line){ + c = ' '; + break; + } + c = line[linep++]; + if(!ralpha(c)) + break; + } + peekrune = c; + lex.lval.var = lookup(0); + return VAR; + } + } + return c; +} + +# +# all characters that have some +# meaning. rest are usable as names +# +ralpha(c: int): int +{ + case c { + 0 or + '+' or + '-' or + '*' or + '/' or + '[' or + ']' or + '(' or + ')' or + '^' or + ':' or + '?' or + ' ' or + '\t' or + '.' or + '|' or + '#' or + '¹' or + 'ⁱ' or + '²' or + '⁲' or + '³' or + '⁳' or + '×' or + '÷' => + return 0; + } + return 1; +} + +gdigit(nil: ref YYLEX): int +{ + c := digval; + if(c) { + digval = 0; + return c; + } + if(linep >= len line) + return 0; + return line[linep++]; +} + +YYLEX.error(lex: self ref YYLEX, s: string) +{ + # + # hack to intercept message from yaccpar + # + if(s == "syntax error") { + lex.error(sys->sprint("syntax error, last name: %s", sym)); + return; + } + sys->print("%d: %s\n\t%s\n", lineno, line, s); + nerrors++; + if(nerrors > 5) { + sys->print("too many errors\n"); + raise "fail:errors"; + } +} + +yyerror(s: string) +{ + l := ref YYLEX; + l.error(s); +} + +Node.mk(v: real): Node +{ + return (v, array[Ndim] of {* => 0}); +} + +Node.add(a: self Node, b: Node): Node +{ + c := Node.mk(fadd(a.val, b.val)); + for(i:=0; isprint(" [%d]", d); + case n { + 1 => + ; + 2 => + s += "²"; + 3 => + s += "³"; + 4 => + s += "⁴"; + * => + s += sys->sprint("^%d", n); + } + } + return s; +} + +Node.text(n: self Node): string +{ + str := sys->sprint("%.7g", n.val); + f := 0; + for(i:=1; i 0) + str += printdim(i, d); + else if(d < 0) + f = 1; + } + + if(f) { + str += " /"; + for(i=1; i= len sym || p[j] != sym[j]) + continue Pref; + sym = sym[j:]; + return prefix[i].val; + } + + # + # rip off 's' suffixes + # + for(j:=0; j < len sym; j++) + ; + j--; + # j>1 is special hack to disallow ms finding m + if(j > 1 && sym[j] == 's') { + sym = sym[0:j]; + return 1.0; + } + return 0.0; +} + +# +# reads a floating-point number +# + +readreal[T](f: ref fn(t: T): int, vp: T): (real, int) +{ + s := ""; + c := f(vp); + while(c == ' ' || c == '\t') + c = f(vp); + if(c == '-' || c == '+'){ + s[len s] = c; + c = f(vp); + } + start := len s; + while(c >= '0' && c <= '9'){ + s[len s] = c; + c = f(vp); + } + if(c == '.'){ + s[len s] = c; + c = f(vp); + while(c >= '0' && c <= '9'){ + s[len s] = c; + c = f(vp); + } + } + if(len s > start && (c == 'e' || c == 'E')){ + s[len s] = c; + c = f(vp); + if(c == '-' || c == '+'){ + s[len s] = c; + c = f(vp); + } + while(c >= '0' && c <= '9'){ + s[len s] = c; + c = f(vp); + } + } + return (real s, c); +} + +# +# careful floating point +# + +fmul(a, b: real): real +{ + l: real; + + if(a <= 0.0) { + if(a == 0.0) + return 0.0; + l = math->log(-a); + } else + l = math->log(a); + + if(b <= 0.0) { + if(b == 0.0) + return 0.0; + l += math->log(-b); + } else + l += math->log(b); + + if(l > Maxe) { + yyerror("overflow in multiply"); + return 1.0; + } + if(l < -Maxe) { + yyerror("underflow in multiply"); + return 0.0; + } + return a*b; +} + +fdiv(a, b: real): real +{ + l: real; + + if(a <= 0.0) { + if(a == 0.0) + return 0.0; + l = math->log(-a); + } else + l = math->log(a); + + if(b <= 0.0) { + if(b == 0.0) { + yyerror("division by zero"); + return 1.0; + } + l -= math->log(-b); + } else + l -= math->log(b); + + if(l > Maxe) { + yyerror("overflow in divide"); + return 1.0; + } + if(l < -Maxe) { + yyerror("underflow in divide"); + return 0.0; + } + return a/b; +} + +fadd(a, b: real): real +{ + return a + b; +} -- cgit v1.2.3