// 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 prog expr expr0 expr1 expr2 expr3 expr4 %token VAL %token VAR %token 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 { 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 = 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 0 { str = printdim(str, i, d); } else if d < 0 { f = 1; } } if f != 0 { str += " /"; for i=1; i= 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 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; }