1
0
mirror of https://github.com/golang/go synced 2024-11-20 07:54:39 -07:00
go/src/cmd/yacc/units.y
Benny Siegert f6d582db61 cmd/yacc: spring cleaning for units.y
* sort imports
* use runtime.GOROOT
* fix some typos

R=golang-dev, dave, rsc
CC=golang-dev
https://golang.org/cl/5987054
2012-04-09 15:04:59 -04:00

764 lines
11 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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 (
"bufio"
"flag"
"fmt"
"math"
"runtime"
"os"
"path/filepath"
"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()
file = filepath.Join(runtime.GOROOT(), "src/cmd/yacc/units.txt")
if flag.NArg() > 0 {
file = flag.Arg(0)
} else if file == "" {
fmt.Fprintf(os.Stderr, "cannot 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
* develop 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
}