diff options
Diffstat (limited to 'src/cmd/yacc/units.y')
-rw-r--r-- | src/cmd/yacc/units.y | 763 |
1 files changed, 763 insertions, 0 deletions
diff --git a/src/cmd/yacc/units.y b/src/cmd/yacc/units.y new file mode 100644 index 000000000..7258e3e59 --- /dev/null +++ b/src/cmd/yacc/units.y @@ -0,0 +1,763 @@ +// 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 + +// Generate parser with prefix "units_": +// go tool yacc -p "units_" + +%{ + +// units.y +// example of a Go yacc program +// usage is +// go tool yacc -p "units_" units.y (produces y.go) +// go build -o units y.go +// ./units $GOROOT/src/cmd/yacc/units.txt +// you have: c +// you want: furlongs/fortnight +// * 1.8026178e+12 +// / 5.5474878e-13 +// you have: + +package main + +import ( + "flag" + "fmt" + "bufio" + "os" + "math" + "strconv" + "unicode/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 rune // 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 { + Errorf("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 { + Errorf("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 { + Errorf("undefined %v", $1.name) + $$ = one + } else { + $$ = $1.node + } + } +| VAL + { + $$ = one + $$.vval = $1 + } +| '(' expr ')' + { + $$ = $2 + } +%% + +type UnitsLex int + +func (UnitsLex) Lex(yylval *units_SymType) int { + var c rune + var 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 int(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.ParseFloat(sym, 64) + if err != nil { + fmt.Printf("error converting %v\n", sym) + f = 0 + } + yylval.vval = f + return VAL +} + +func (UnitsLex) Error(s string) { + Errorf("syntax error, last name: %v", sym) +} + +func main() { + var file string + + flag.BoolVar(&vflag, "v", false, "verbose") + + flag.Parse() + + if dir := os.Getenv("GOROOT"); dir != "" { + file = dir + "/src/cmd/yacc/units.txt" + } + if flag.NArg() > 0 { + file = flag.Arg(0) + } else if file == "" { + fmt.Fprintf(os.Stderr, "can not find data file units.txt; provide it as argument or set $GOROOT\n") + os.Exit(1) + } + + f, err := os.Open(file) + if err != nil { + fmt.Fprintf(os.Stderr, "error opening %v: %v\n", 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 = ':' + units_Parse(UnitsLex(0)) + } + + /* + * 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 + units_Parse(UnitsLex(0)) + 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 rune) bool { + switch c { + case 0, '+', '-', '*', '/', '[', ']', '(', ')', + '^', ':', '?', ' ', '\t', '.', '|', '#', + '×', '÷', '¹', 'ⁱ', '²', '', '³', '': + return false + } + return true +} + +/* + * number forming character + */ +func rdigit(c rune) bool { + switch c { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '.', 'e', '+', '-': + return true + } + return false +} + +func Errorf(s string, v ...interface{}) { + 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 Error(s string) { + Errorf("%s", s) +} + +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() rune { + var c rune + var 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 + {1e-24, "yocto"}, + {1e-21, "zepto"}, + {1e-18, "atto"}, + {1e-15, "femto"}, + {1e-12, "pico"}, + {1e-9, "nano"}, + {1e-6, "micro"}, + {1e-6, "μ"}, + {1e-3, "milli"}, + {1e-2, "centi"}, + {1e-1, "deci"}, + {1e1, "deka"}, + {1e2, "hecta"}, + {1e2, "hecto"}, + {1e3, "kilo"}, + {1e6, "mega"}, + {1e6, "meg"}, + {1e9, "giga"}, + {1e12, "tera"}, + {1e15, "peta"}, + {1e18, "exa"}, + {1e21, "zetta"}, + {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 { + Errorf("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 +} |