| // Derived from Plan 9's /sys/src/cmd/units.y |
| // http://plan9.bell-labs.com/sources/plan9/sys/src/cmd/units.y |
| // |
| // Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights Reserved. |
| // Portions Copyright 2009 The Go Authors. All Rights Reserved. |
| // Distributed under the terms of the Lucent Public License Version 1.02 |
| // See http://plan9.bell-labs.com/plan9/license.html |
| |
| %{ |
| |
| // units.y |
| // example of a goyacc program |
| // usage is |
| // goyacc units.y (produces y.go) |
| // 6g y.go |
| // 6l y.6 |
| // ./6.out $GOROOT/src/cmd/goyacc/units |
| // you have: c |
| // you want: furlongs/fortnight |
| // * 1.8026178e+12 |
| // / 5.5474878e-13 |
| // you have: |
| |
| package main |
| |
| import |
| ( |
| "flag"; |
| "fmt"; |
| "bufio"; |
| "os"; |
| "math"; |
| "strconv"; |
| "utf8"; |
| ) |
| |
| const |
| ( |
| Ndim = 15; // number of dimensions |
| Maxe = 695; // log of largest number |
| ) |
| |
| type Node |
| struct |
| { |
| vval float64; |
| dim [Ndim]int8; |
| } |
| |
| type Var |
| struct |
| { |
| name string; |
| node Node; |
| } |
| |
| var fi *bufio.Reader // input |
| var fund [Ndim]*Var // names of fundamental units |
| var line string // current input line |
| var lineno int // current input line number |
| var linep int // index to next rune in unput |
| var nerrors int // error count |
| var one Node // constant one |
| var peekrune int // backup runt from input |
| var retnode1 Node |
| var retnode2 Node |
| var retnode Node |
| var sym string |
| var vflag bool |
| |
| %} |
| |
| %union |
| { |
| node Node; |
| vvar *Var; |
| numb int; |
| vval float64; |
| } |
| |
| %type <node> prog expr expr0 expr1 expr2 expr3 expr4 |
| |
| %token <vval> VAL |
| %token <vvar> VAR |
| %token <numb> SUP |
| %% |
| prog: |
| ':' VAR expr |
| { |
| var f int; |
| |
| f = int($2.node.dim[0]); |
| $2.node = $3; |
| $2.node.dim[0] = 1; |
| if f != 0 { |
| Error("redefinition of %v", $2.name); |
| } else |
| if vflag { |
| fmt.Printf("%v\t%v\n", $2.name, &$2.node); |
| } |
| } |
| | ':' VAR '#' |
| { |
| var f, i int; |
| |
| for i=1; i<Ndim; i++ { |
| if fund[i] == nil { |
| break; |
| } |
| } |
| if i >= Ndim { |
| Error("too many dimensions"); |
| i = Ndim-1; |
| } |
| fund[i] = $2; |
| |
| f = int($2.node.dim[0]); |
| $2.node = one; |
| $2.node.dim[0] = 1; |
| $2.node.dim[i] = 1; |
| if f != 0 { |
| Error("redefinition of %v", $2.name); |
| } else |
| if vflag { |
| fmt.Printf("%v\t#\n", $2.name); |
| } |
| } |
| | ':' |
| { |
| } |
| | '?' expr |
| { |
| retnode1 = $2; |
| } |
| | '?' |
| { |
| retnode1 = one; |
| } |
| |
| expr: |
| expr4 |
| | expr '+' expr4 |
| { |
| add(&$$, &$1, &$3); |
| } |
| | expr '-' expr4 |
| { |
| sub(&$$, &$1, &$3); |
| } |
| |
| expr4: |
| expr3 |
| | expr4 '*' expr3 |
| { |
| mul(&$$, &$1, &$3); |
| } |
| | expr4 '/' expr3 |
| { |
| div(&$$, &$1, &$3); |
| } |
| |
| expr3: |
| expr2 |
| | expr3 expr2 |
| { |
| mul(&$$, &$1, &$2); |
| } |
| |
| expr2: |
| expr1 |
| | expr2 SUP |
| { |
| xpn(&$$, &$1, $2); |
| } |
| | expr2 '^' expr1 |
| { |
| var i int; |
| |
| for i=1; i<Ndim; i++ { |
| if $3.dim[i] != 0 { |
| Error("exponent has units"); |
| $$ = $1; |
| break; |
| } |
| } |
| if i >= Ndim { |
| i = int($3.vval); |
| if float64(i) != $3.vval { |
| Error("exponent not integral"); |
| } |
| xpn(&$$, &$1, i); |
| } |
| } |
| |
| expr1: |
| expr0 |
| | expr1 '|' expr0 |
| { |
| div(&$$, &$1, &$3); |
| } |
| |
| expr0: |
| VAR |
| { |
| if $1.node.dim[0] == 0 { |
| Error("undefined %v", $1.name); |
| $$ = one; |
| } else |
| $$ = $1.node; |
| } |
| | VAL |
| { |
| $$ = one; |
| $$.vval = $1; |
| } |
| | '(' expr ')' |
| { |
| $$ = $2; |
| } |
| %% |
| |
| func |
| Lex() int |
| { |
| var c, i int; |
| |
| c = peekrune; |
| peekrune = ' '; |
| |
| loop: |
| if (c >= '0' && c <= '9') || c == '.' { |
| goto numb; |
| } |
| if ralpha(c) { |
| goto alpha; |
| } |
| switch c { |
| case ' ', '\t': |
| c = getrune(); |
| goto loop; |
| case '×': |
| return '*'; |
| case '÷': |
| return '/'; |
| case '¹', 'ⁱ': |
| yylval.numb = 1; |
| return SUP; |
| case '²', '': |
| yylval.numb = 2; |
| return SUP; |
| case '³', '': |
| yylval.numb = 3; |
| return SUP; |
| } |
| return c; |
| |
| alpha: |
| sym = ""; |
| for i=0;; i++ { |
| sym += string(c); |
| c = getrune(); |
| if !ralpha(c) { |
| break; |
| } |
| } |
| peekrune = c; |
| yylval.vvar = lookup(0); |
| return VAR; |
| |
| numb: |
| sym = ""; |
| for i=0;; i++ { |
| sym += string(c); |
| c = getrune(); |
| if !rdigit(c) { |
| break; |
| } |
| } |
| peekrune = c; |
| f, err := strconv.Atof64(sym); |
| if err != nil { |
| fmt.Printf("error converting %v", sym); |
| f = 0; |
| } |
| yylval.vval = f; |
| return VAL; |
| } |
| |
| func |
| main() |
| { |
| var file string; |
| |
| flag.BoolVar(&vflag, "v", false, "verbose"); |
| |
| flag.Parse(); |
| |
| file = os.Getenv("GOROOT") + "/src/cmd/goyacc/units.txt"; |
| if flag.NArg() > 0 { |
| file = flag.Arg(0); |
| } |
| |
| f,err := os.Open(file, os.O_RDONLY, 0); |
| if err != nil { |
| fmt.Printf("error opening %v: %v", file, err); |
| os.Exit(1); |
| } |
| fi = bufio.NewReader(f); |
| |
| one.vval = 1; |
| |
| /* |
| * read the 'units' file to |
| * develope a database |
| */ |
| lineno = 0; |
| for { |
| lineno++; |
| if readline() { |
| break; |
| } |
| if len(line) == 0 || line[0] == '/' { |
| continue; |
| } |
| peekrune = ':'; |
| Parse(); |
| } |
| |
| /* |
| * read the console to |
| * print ratio of pairs |
| */ |
| fi = bufio.NewReader(os.NewFile(0, "stdin")); |
| |
| lineno = 0; |
| for { |
| if (lineno & 1) != 0 { |
| fmt.Printf("you want: "); |
| } else |
| fmt.Printf("you have: "); |
| if readline() { |
| break; |
| } |
| peekrune = '?'; |
| nerrors = 0; |
| Parse(); |
| if nerrors != 0 { |
| continue; |
| } |
| if (lineno & 1) != 0 { |
| if specialcase(&retnode, &retnode2, &retnode1) { |
| fmt.Printf("\tis %v\n", &retnode); |
| } else { |
| div(&retnode, &retnode2, &retnode1); |
| fmt.Printf("\t* %v\n", &retnode); |
| div(&retnode, &retnode1, &retnode2); |
| fmt.Printf("\t/ %v\n", &retnode); |
| } |
| } else |
| retnode2 = retnode1; |
| lineno++; |
| } |
| fmt.Printf("\n"); |
| os.Exit(0); |
| } |
| |
| /* |
| * all characters that have some |
| * meaning. rest are usable as names |
| */ |
| func |
| ralpha(c int) bool |
| { |
| switch c { |
| case 0, '+', '-', '*', '/', '[', ']', '(', ')', |
| '^', ':', '?', ' ', '\t', '.', '|', '#', |
| '×', '÷', '¹', 'ⁱ', '²', '', '³', '': |
| return false; |
| } |
| return true; |
| } |
| |
| /* |
| * number forming character |
| */ |
| func |
| rdigit(c int) bool |
| { |
| switch c { |
| case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', |
| '.', 'e', '+', '-': |
| return true; |
| } |
| return false; |
| } |
| |
| func |
| Error(s string, v ...) |
| { |
| |
| /* |
| * hack to intercept message from yaccpar |
| */ |
| if s == "syntax error" { |
| Error("syntax error, last name: %v", sym); |
| return; |
| } |
| fmt.Printf("%v: %v\n\t", lineno, line); |
| fmt.Printf(s, v); |
| fmt.Printf("\n"); |
| |
| nerrors++; |
| if nerrors > 5 { |
| fmt.Printf("too many errors\n"); |
| os.Exit(1); |
| } |
| } |
| |
| func |
| add(c,a,b *Node) |
| { |
| var i int; |
| var d int8; |
| |
| for i=0; i<Ndim; i++ { |
| d = a.dim[i]; |
| c.dim[i] = d; |
| if d != b.dim[i] { |
| Error("add must be like units"); |
| } |
| } |
| c.vval = fadd(a.vval, b.vval); |
| } |
| |
| func |
| sub(c,a,b *Node) |
| { |
| var i int; |
| var d int8; |
| |
| for i=0; i<Ndim; i++ { |
| d = a.dim[i]; |
| c.dim[i] = d; |
| if d != b.dim[i] { |
| Error("sub must be like units"); |
| } |
| } |
| c.vval = fadd(a.vval, -b.vval); |
| } |
| |
| func |
| mul(c,a,b *Node) |
| { |
| var i int; |
| |
| for i=0; i<Ndim; i++ { |
| c.dim[i] = a.dim[i] + b.dim[i]; |
| } |
| c.vval = fmul(a.vval, b.vval); |
| } |
| |
| func |
| div(c,a,b *Node) |
| { |
| var i int; |
| |
| for i=0; i<Ndim; i++ { |
| c.dim[i] = a.dim[i] - b.dim[i]; |
| } |
| c.vval = fdiv(a.vval, b.vval); |
| } |
| |
| func |
| xpn(c,a *Node, b int) |
| { |
| var i int; |
| |
| *c = one; |
| if b < 0 { |
| b = -b; |
| for i=0; i<b; i++ { |
| div(c, c, a); |
| } |
| } else |
| for i=0; i<b; i++ { |
| mul(c, c, a); |
| } |
| } |
| |
| func |
| specialcase(c,a,b *Node) bool |
| { |
| var i int; |
| var d, d1, d2 int8; |
| |
| d1 = 0; |
| d2 = 0; |
| for i=1; i<Ndim; i++ { |
| d = a.dim[i]; |
| if d != 0 { |
| if d != 1 || d1 != 0 { |
| return false; |
| } |
| d1 = int8(i); |
| } |
| d = b.dim[i]; |
| if d != 0 { |
| if d != 1 || d2 != 0 { |
| return false; |
| } |
| d2 = int8(i); |
| } |
| } |
| if d1 == 0 || d2 == 0 { |
| return false; |
| } |
| |
| if fund[d1].name == "°C" && fund[d2].name == "°F" && |
| b.vval == 1 { |
| for ll:=0; ll<len(c.dim); ll++ { |
| c.dim[ll] = b.dim[ll]; |
| } |
| c.vval = a.vval * 9. / 5. + 32.; |
| return true; |
| } |
| |
| if fund[d1].name == "°F" && fund[d2].name == "°C" && |
| b.vval == 1 { |
| for ll:=0; ll<len(c.dim); ll++ { |
| c.dim[ll] = b.dim[ll]; |
| } |
| c.vval = (a.vval - 32.) * 5. / 9.; |
| return true; |
| } |
| return false; |
| } |
| |
| func |
| printdim(str string, d, n int) string |
| { |
| var v *Var; |
| |
| if n != 0 { |
| v = fund[d]; |
| if v != nil { |
| str += fmt.Sprintf("%v", v.name); |
| } else |
| str += fmt.Sprintf("[%d]", d); |
| switch n { |
| case 1: |
| break; |
| case 2: |
| str += "²"; |
| case 3: |
| str += "³"; |
| default: |
| str += fmt.Sprintf("^%d", n); |
| } |
| } |
| return str; |
| } |
| |
| func (n Node) |
| String() string |
| { |
| var str string; |
| var f, i, d int; |
| |
| str = fmt.Sprintf("%.7e ", n.vval); |
| |
| f = 0; |
| for i=1; i<Ndim; i++ { |
| d = int(n.dim[i]); |
| if d > 0 { |
| str = printdim(str, i, d); |
| } else |
| if d < 0 { |
| f = 1; |
| } |
| } |
| |
| if f != 0 { |
| str += " /"; |
| for i=1; i<Ndim; i++ { |
| d = int(n.dim[i]); |
| if d < 0 { |
| str = printdim(str, i, -d); |
| } |
| } |
| } |
| |
| return str; |
| } |
| |
| func (v *Var) |
| String() string |
| { |
| var str string; |
| str = fmt.Sprintf("%v %v", v.name, v.node); |
| return str; |
| } |
| |
| func |
| readline() bool |
| { |
| s,err := fi.ReadString('\n'); |
| if err != nil { |
| return true; |
| } |
| line = s; |
| linep = 0; |
| return false; |
| } |
| |
| func |
| getrune() int |
| { |
| var c,n int; |
| |
| if linep >= len(line) { |
| return 0; |
| } |
| c,n = utf8.DecodeRuneInString(line[linep:len(line)]); |
| linep += n; |
| if c == '\n' { |
| c = 0; |
| } |
| return c; |
| } |
| |
| var symmap = make(map[string]*Var); // symbol table |
| |
| func |
| lookup(f int) *Var |
| { |
| var p float64; |
| var w *Var; |
| |
| v,ok := symmap[sym]; |
| if ok { |
| return v; |
| } |
| if f != 0 { |
| return nil; |
| } |
| v = new(Var); |
| v.name = sym; |
| symmap[sym] = v; |
| |
| p = 1; |
| for { |
| p = fmul(p, pname()); |
| if p == 0 { |
| break; |
| } |
| w = lookup(1); |
| if w != nil { |
| v.node = w.node; |
| v.node.vval = fmul(v.node.vval, p); |
| break; |
| } |
| } |
| return v; |
| } |
| |
| type Prefix |
| struct |
| { |
| vval float64; |
| name string; |
| } |
| |
| var prefix = []Prefix { // prefix table |
| Prefix { 1e-24, "yocto" }, |
| Prefix { 1e-21, "zepto" }, |
| Prefix { 1e-18, "atto" }, |
| Prefix { 1e-15, "femto" }, |
| Prefix { 1e-12, "pico" }, |
| Prefix { 1e-9, "nano" }, |
| Prefix { 1e-6, "micro" }, |
| Prefix { 1e-6, "μ" }, |
| Prefix { 1e-3, "milli" }, |
| Prefix { 1e-2, "centi" }, |
| Prefix { 1e-1, "deci" }, |
| Prefix { 1e1, "deka" }, |
| Prefix { 1e2, "hecta" }, |
| Prefix { 1e2, "hecto" }, |
| Prefix { 1e3, "kilo" }, |
| Prefix { 1e6, "mega" }, |
| Prefix { 1e6, "meg" }, |
| Prefix { 1e9, "giga" }, |
| Prefix { 1e12, "tera" }, |
| Prefix { 1e15, "peta" }, |
| Prefix { 1e18, "exa" }, |
| Prefix { 1e21, "zetta" }, |
| Prefix { 1e24, "yotta" } |
| } |
| |
| func |
| pname() float64 |
| { |
| var i, j, n int; |
| var s string; |
| |
| /* |
| * rip off normal prefixs |
| */ |
| n = len(sym); |
| for i=0; i<len(prefix); i++ { |
| s = prefix[i].name; |
| j = len(s); |
| if j < n && sym[0:j] == s { |
| sym = sym[j:n]; |
| return prefix[i].vval; |
| } |
| } |
| |
| /* |
| * rip off 's' suffixes |
| */ |
| if n > 2 && sym[n-1] == 's' { |
| sym = sym[0:n-1]; |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| |
| // careful multiplication |
| // exponents (log) are checked before multiply |
| func |
| fmul(a, b float64) float64 |
| { |
| var l float64; |
| |
| if b <= 0 { |
| if b == 0 { |
| return 0; |
| } |
| l = math.Log(-b); |
| } else |
| l = math.Log(b); |
| |
| if a <= 0 { |
| if a == 0 { |
| return 0; |
| } |
| l += math.Log(-a); |
| } else |
| l += math.Log(a); |
| |
| if l > Maxe { |
| Error("overflow in multiply"); |
| return 1; |
| } |
| if l < -Maxe { |
| Error("underflow in multiply"); |
| return 0; |
| } |
| return a*b; |
| } |
| |
| // careful division |
| // exponents (log) are checked before divide |
| func |
| fdiv(a, b float64) float64 |
| { |
| var l float64; |
| |
| if b <= 0 { |
| if b == 0 { |
| Error("division by zero: %v %v", a, b); |
| return 1; |
| } |
| l = math.Log(-b); |
| } else |
| l = math.Log(b); |
| |
| if a <= 0 { |
| if a == 0 { |
| return 0; |
| } |
| l -= math.Log(-a); |
| } else |
| l -= math.Log(a); |
| |
| if l < -Maxe { |
| Error("overflow in divide"); |
| return 1; |
| } |
| if l > Maxe { |
| Error("underflow in divide"); |
| return 0; |
| } |
| return a/b; |
| } |
| |
| func |
| fadd(a, b float64) float64 |
| { |
| return a + b; |
| } |