github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/cmd/yacc/units.y (about)

     1  // Derived from Plan 9's /sys/src/cmd/units.y
     2  // http://plan9.bell-labs.com/sources/plan9/sys/src/cmd/units.y
     3  //
     4  // Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights Reserved.
     5  // Portions Copyright 2009 The Go Authors.  All Rights Reserved.
     6  // Distributed under the terms of the Lucent Public License Version 1.02
     7  // See http://plan9.bell-labs.com/plan9/license.html
     8  
     9  // Generate parser with prefix "units_":
    10  //	go tool yacc -p "units_"
    11  
    12  %{
    13  
    14  // This tag will end up in the generated y.go, so that forgetting
    15  // 'make clean' does not fail the next build.
    16  
    17  // +build ignore
    18  
    19  // units.y
    20  // example of a Go yacc program
    21  // usage is
    22  //	go tool yacc -p "units_" units.y (produces y.go)
    23  //	go build -o units y.go
    24  //	./units $GOROOT/src/cmd/yacc/units.txt
    25  //	you have: c
    26  //	you want: furlongs/fortnight
    27  //		* 1.8026178e+12
    28  //		/ 5.5474878e-13
    29  //	you have:
    30  
    31  package main
    32  
    33  import (
    34  	"bufio"
    35  	"flag"
    36  	"fmt"
    37  	"math"
    38  	"runtime"
    39  	"os"
    40  	"path/filepath"
    41  	"strconv"
    42  	"unicode/utf8"
    43  )
    44  
    45  const (
    46  	Ndim = 15  // number of dimensions
    47  	Maxe = 695 // log of largest number
    48  )
    49  
    50  type Node struct {
    51  	vval float64
    52  	dim  [Ndim]int8
    53  }
    54  
    55  type Var struct {
    56  	name string
    57  	node Node
    58  }
    59  
    60  var fi *bufio.Reader // input
    61  var fund [Ndim]*Var  // names of fundamental units
    62  var line string      // current input line
    63  var lineno int       // current input line number
    64  var linep int        // index to next rune in unput
    65  var nerrors int      // error count
    66  var one Node         // constant one
    67  var peekrune rune    // backup runt from input
    68  var retnode1 Node
    69  var retnode2 Node
    70  var retnode Node
    71  var sym string
    72  var vflag bool
    73  %}
    74  
    75  %union {
    76  	node Node
    77  	vvar *Var
    78  	numb int
    79  	vval float64
    80  }
    81  
    82  %type	<node>	prog expr expr0 expr1 expr2 expr3 expr4
    83  
    84  %token	<vval>	VÄL // dieresis to test UTF-8
    85  %token	<vvar>	VAR
    86  %token	<numb>	_SUP // tests leading underscore in token name
    87  %%
    88  prog:
    89  	':' VAR expr
    90  	{
    91  		var f int
    92  		f = int($2.node.dim[0])
    93  		$2.node = $3
    94  		$2.node.dim[0] = 1
    95  		if f != 0 {
    96  			Errorf("redefinition of %v", $2.name)
    97  		} else if vflag {
    98  			fmt.Printf("%v\t%v\n", $2.name, &$2.node)
    99  		}
   100  	}
   101  |	':' VAR '#'
   102  	{
   103  		var f, i int
   104  		for i = 1; i < Ndim; i++ {
   105  			if fund[i] == nil {
   106  				break
   107  			}
   108  		}
   109  		if i >= Ndim {
   110  			Error("too many dimensions")
   111  			i = Ndim - 1
   112  		}
   113  		fund[i] = $2
   114  		f = int($2.node.dim[0])
   115  		$2.node = one
   116  		$2.node.dim[0] = 1
   117  		$2.node.dim[i] = 1
   118  		if f != 0 {
   119  			Errorf("redefinition of %v", $2.name)
   120  		} else if vflag {
   121  			fmt.Printf("%v\t#\n", $2.name)
   122  		}
   123  	}
   124  |	':'
   125  	{
   126  	}
   127  |	'?' expr
   128  	{
   129  		retnode1 = $2
   130  	}
   131  |	'?'
   132  	{
   133  		retnode1 = one
   134  	}
   135  
   136  expr:
   137  	expr4
   138  |	expr '+' expr4
   139  	{
   140  		add(&$$, &$1, &$3)
   141  	}
   142  |	expr '-' expr4
   143  	{
   144  		sub(&$$, &$1, &$3)
   145  	}
   146  
   147  expr4:
   148  	expr3
   149  |	expr4 '*' expr3
   150  	{
   151  		mul(&$$, &$1, &$3)
   152  	}
   153  |	expr4 '/' expr3
   154  	{
   155  		div(&$$, &$1, &$3)
   156  	}
   157  
   158  expr3:
   159  	expr2
   160  |	expr3 expr2
   161  	{
   162  		mul(&$$, &$1, &$2)
   163  	}
   164  
   165  expr2:
   166  	expr1
   167  |	expr2 _SUP
   168  	{
   169  		xpn(&$$, &$1, $2)
   170  	}
   171  |	expr2 '^' expr1
   172  	{
   173  		var i int
   174  		for i = 1; i < Ndim; i++ {
   175  			if $3.dim[i] != 0 {
   176  				Error("exponent has units")
   177  				$$ = $1
   178  				break
   179  			}
   180  		}
   181  		if i >= Ndim {
   182  			i = int($3.vval)
   183  			if float64(i) != $3.vval {
   184  				Error("exponent not integral")
   185  			}
   186  			xpn(&$$, &$1, i)
   187  		}
   188  	}
   189  
   190  expr1:
   191  	expr0
   192  |	expr1 '|' expr0
   193  	{
   194  		div(&$$, &$1, &$3)
   195  	}
   196  
   197  expr0:
   198  	VAR
   199  	{
   200  		if $1.node.dim[0] == 0 {
   201  			Errorf("undefined %v", $1.name)
   202  			$$ = one
   203  		} else {
   204  			$$ = $1.node
   205  		}
   206  	}
   207  |	VÄL
   208  	{
   209  		$$ = one
   210  		$$.vval = $1
   211  	}
   212  |	'(' expr ')'
   213  	{
   214  		$$ = $2
   215  	}
   216  %%
   217  
   218  type UnitsLex int
   219  
   220  func (UnitsLex) Lex(yylval *units_SymType) int {
   221  	var c rune
   222  	var i int
   223  
   224  	c = peekrune
   225  	peekrune = ' '
   226  
   227  loop:
   228  	if (c >= '0' && c <= '9') || c == '.' {
   229  		goto numb
   230  	}
   231  	if ralpha(c) {
   232  		goto alpha
   233  	}
   234  	switch c {
   235  	case ' ', '\t':
   236  		c = getrune()
   237  		goto loop
   238  	case '×':
   239  		return '*'
   240  	case '÷':
   241  		return '/'
   242  	case '¹', 'ⁱ':
   243  		yylval.numb = 1
   244  		return _SUP
   245  	case '²', '⁲':
   246  		yylval.numb = 2
   247  		return _SUP
   248  	case '³', '⁳':
   249  		yylval.numb = 3
   250  		return _SUP
   251  	}
   252  	return int(c)
   253  
   254  alpha:
   255  	sym = ""
   256  	for i = 0; ; i++ {
   257  		sym += string(c)
   258  		c = getrune()
   259  		if !ralpha(c) {
   260  			break
   261  		}
   262  	}
   263  	peekrune = c
   264  	yylval.vvar = lookup(0)
   265  	return VAR
   266  
   267  numb:
   268  	sym = ""
   269  	for i = 0; ; i++ {
   270  		sym += string(c)
   271  		c = getrune()
   272  		if !rdigit(c) {
   273  			break
   274  		}
   275  	}
   276  	peekrune = c
   277  	f, err := strconv.ParseFloat(sym, 64)
   278  	if err != nil {
   279  		fmt.Printf("error converting %v\n", sym)
   280  		f = 0
   281  	}
   282  	yylval.vval = f
   283  	return VÄL
   284  }
   285  
   286  func (UnitsLex) Error(s string) {
   287  	Errorf("syntax error, last name: %v", sym)
   288  }
   289  
   290  func main() {
   291  	var file string
   292  
   293  	flag.BoolVar(&vflag, "v", false, "verbose")
   294  
   295  	flag.Parse()
   296  
   297  	file = filepath.Join(runtime.GOROOT(), "src/cmd/yacc/units.txt")
   298  	if flag.NArg() > 0 {
   299  		file = flag.Arg(0)
   300  	} else if file == "" {
   301  		fmt.Fprintf(os.Stderr, "cannot find data file units.txt; provide it as argument or set $GOROOT\n")
   302  		os.Exit(1)
   303  	}
   304  
   305  	f, err := os.Open(file)
   306  	if err != nil {
   307  		fmt.Fprintf(os.Stderr, "error opening %v: %v\n", file, err)
   308  		os.Exit(1)
   309  	}
   310  	fi = bufio.NewReader(f)
   311  
   312  	one.vval = 1
   313  
   314  	/*
   315  	 * read the 'units' file to
   316  	 * develop a database
   317  	 */
   318  	lineno = 0
   319  	for {
   320  		lineno++
   321  		if readline() {
   322  			break
   323  		}
   324  		if len(line) == 0 || line[0] == '/' {
   325  			continue
   326  		}
   327  		peekrune = ':'
   328  		units_Parse(UnitsLex(0))
   329  	}
   330  
   331  	/*
   332  	 * read the console to
   333  	 * print ratio of pairs
   334  	 */
   335  	fi = bufio.NewReader(os.NewFile(0, "stdin"))
   336  
   337  	lineno = 0
   338  	for {
   339  		if (lineno & 1) != 0 {
   340  			fmt.Printf("you want: ")
   341  		} else {
   342  			fmt.Printf("you have: ")
   343  		}
   344  		if readline() {
   345  			break
   346  		}
   347  		peekrune = '?'
   348  		nerrors = 0
   349  		units_Parse(UnitsLex(0))
   350  		if nerrors != 0 {
   351  			continue
   352  		}
   353  		if (lineno & 1) != 0 {
   354  			if specialcase(&retnode, &retnode2, &retnode1) {
   355  				fmt.Printf("\tis %v\n", &retnode)
   356  			} else {
   357  				div(&retnode, &retnode2, &retnode1)
   358  				fmt.Printf("\t* %v\n", &retnode)
   359  				div(&retnode, &retnode1, &retnode2)
   360  				fmt.Printf("\t/ %v\n", &retnode)
   361  			}
   362  		} else {
   363  			retnode2 = retnode1
   364  		}
   365  		lineno++
   366  	}
   367  	fmt.Printf("\n")
   368  	os.Exit(0)
   369  }
   370  
   371  /*
   372   * all characters that have some
   373   * meaning. rest are usable as names
   374   */
   375  func ralpha(c rune) bool {
   376  	switch c {
   377  	case 0, '+', '-', '*', '/', '[', ']', '(', ')',
   378  		'^', ':', '?', ' ', '\t', '.', '|', '#',
   379  		'×', '÷', '¹', 'ⁱ', '²', '⁲', '³', '⁳':
   380  		return false
   381  	}
   382  	return true
   383  }
   384  
   385  /*
   386   * number forming character
   387   */
   388  func rdigit(c rune) bool {
   389  	switch c {
   390  	case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
   391  		'.', 'e', '+', '-':
   392  		return true
   393  	}
   394  	return false
   395  }
   396  
   397  func Errorf(s string, v ...interface{}) {
   398  	fmt.Printf("%v: %v\n\t", lineno, line)
   399  	fmt.Printf(s, v...)
   400  	fmt.Printf("\n")
   401  
   402  	nerrors++
   403  	if nerrors > 5 {
   404  		fmt.Printf("too many errors\n")
   405  		os.Exit(1)
   406  	}
   407  }
   408  
   409  func Error(s string) {
   410  	Errorf("%s", s)
   411  }
   412  
   413  func add(c, a, b *Node) {
   414  	var i int
   415  	var d int8
   416  
   417  	for i = 0; i < Ndim; i++ {
   418  		d = a.dim[i]
   419  		c.dim[i] = d
   420  		if d != b.dim[i] {
   421  			Error("add must be like units")
   422  		}
   423  	}
   424  	c.vval = fadd(a.vval, b.vval)
   425  }
   426  
   427  func sub(c, a, b *Node) {
   428  	var i int
   429  	var d int8
   430  
   431  	for i = 0; i < Ndim; i++ {
   432  		d = a.dim[i]
   433  		c.dim[i] = d
   434  		if d != b.dim[i] {
   435  			Error("sub must be like units")
   436  		}
   437  	}
   438  	c.vval = fadd(a.vval, -b.vval)
   439  }
   440  
   441  func mul(c, a, b *Node) {
   442  	var i int
   443  
   444  	for i = 0; i < Ndim; i++ {
   445  		c.dim[i] = a.dim[i] + b.dim[i]
   446  	}
   447  	c.vval = fmul(a.vval, b.vval)
   448  }
   449  
   450  func div(c, a, b *Node) {
   451  	var i int
   452  
   453  	for i = 0; i < Ndim; i++ {
   454  		c.dim[i] = a.dim[i] - b.dim[i]
   455  	}
   456  	c.vval = fdiv(a.vval, b.vval)
   457  }
   458  
   459  func xpn(c, a *Node, b int) {
   460  	var i int
   461  
   462  	*c = one
   463  	if b < 0 {
   464  		b = -b
   465  		for i = 0; i < b; i++ {
   466  			div(c, c, a)
   467  		}
   468  	} else {
   469  		for i = 0; i < b; i++ {
   470  			mul(c, c, a)
   471  		}
   472  	}
   473  }
   474  
   475  func specialcase(c, a, b *Node) bool {
   476  	var i int
   477  	var d, d1, d2 int8
   478  
   479  	d1 = 0
   480  	d2 = 0
   481  	for i = 1; i < Ndim; i++ {
   482  		d = a.dim[i]
   483  		if d != 0 {
   484  			if d != 1 || d1 != 0 {
   485  				return false
   486  			}
   487  			d1 = int8(i)
   488  		}
   489  		d = b.dim[i]
   490  		if d != 0 {
   491  			if d != 1 || d2 != 0 {
   492  				return false
   493  			}
   494  			d2 = int8(i)
   495  		}
   496  	}
   497  	if d1 == 0 || d2 == 0 {
   498  		return false
   499  	}
   500  
   501  	if fund[d1].name == "°C" && fund[d2].name == "°F" &&
   502  		b.vval == 1 {
   503  		for ll := 0; ll < len(c.dim); ll++ {
   504  			c.dim[ll] = b.dim[ll]
   505  		}
   506  		c.vval = a.vval*9./5. + 32.
   507  		return true
   508  	}
   509  
   510  	if fund[d1].name == "°F" && fund[d2].name == "°C" &&
   511  		b.vval == 1 {
   512  		for ll := 0; ll < len(c.dim); ll++ {
   513  			c.dim[ll] = b.dim[ll]
   514  		}
   515  		c.vval = (a.vval - 32.) * 5. / 9.
   516  		return true
   517  	}
   518  	return false
   519  }
   520  
   521  func printdim(str string, d, n int) string {
   522  	var v *Var
   523  
   524  	if n != 0 {
   525  		v = fund[d]
   526  		if v != nil {
   527  			str += fmt.Sprintf("%v", v.name)
   528  		} else {
   529  			str += fmt.Sprintf("[%d]", d)
   530  		}
   531  		switch n {
   532  		case 1:
   533  			break
   534  		case 2:
   535  			str += "²"
   536  		case 3:
   537  			str += "³"
   538  		default:
   539  			str += fmt.Sprintf("^%d", n)
   540  		}
   541  	}
   542  	return str
   543  }
   544  
   545  func (n Node) String() string {
   546  	var str string
   547  	var f, i, d int
   548  
   549  	str = fmt.Sprintf("%.7e ", n.vval)
   550  
   551  	f = 0
   552  	for i = 1; i < Ndim; i++ {
   553  		d = int(n.dim[i])
   554  		if d > 0 {
   555  			str = printdim(str, i, d)
   556  		} else if d < 0 {
   557  			f = 1
   558  		}
   559  	}
   560  
   561  	if f != 0 {
   562  		str += " /"
   563  		for i = 1; i < Ndim; i++ {
   564  			d = int(n.dim[i])
   565  			if d < 0 {
   566  				str = printdim(str, i, -d)
   567  			}
   568  		}
   569  	}
   570  
   571  	return str
   572  }
   573  
   574  func (v *Var) String() string {
   575  	var str string
   576  	str = fmt.Sprintf("%v %v", v.name, v.node)
   577  	return str
   578  }
   579  
   580  func readline() bool {
   581  	s, err := fi.ReadString('\n')
   582  	if err != nil {
   583  		return true
   584  	}
   585  	line = s
   586  	linep = 0
   587  	return false
   588  }
   589  
   590  func getrune() rune {
   591  	var c rune
   592  	var n int
   593  
   594  	if linep >= len(line) {
   595  		return 0
   596  	}
   597  	c, n = utf8.DecodeRuneInString(line[linep:len(line)])
   598  	linep += n
   599  	if c == '\n' {
   600  		c = 0
   601  	}
   602  	return c
   603  }
   604  
   605  var symmap = make(map[string]*Var) // symbol table
   606  
   607  func lookup(f int) *Var {
   608  	var p float64
   609  	var w *Var
   610  
   611  	v, ok := symmap[sym]
   612  	if ok {
   613  		return v
   614  	}
   615  	if f != 0 {
   616  		return nil
   617  	}
   618  	v = new(Var)
   619  	v.name = sym
   620  	symmap[sym] = v
   621  
   622  	p = 1
   623  	for {
   624  		p = fmul(p, pname())
   625  		if p == 0 {
   626  			break
   627  		}
   628  		w = lookup(1)
   629  		if w != nil {
   630  			v.node = w.node
   631  			v.node.vval = fmul(v.node.vval, p)
   632  			break
   633  		}
   634  	}
   635  	return v
   636  }
   637  
   638  type Prefix struct {
   639  	vval float64
   640  	name string
   641  }
   642  
   643  var prefix = []Prefix{ // prefix table
   644  	{1e-24, "yocto"},
   645  	{1e-21, "zepto"},
   646  	{1e-18, "atto"},
   647  	{1e-15, "femto"},
   648  	{1e-12, "pico"},
   649  	{1e-9, "nano"},
   650  	{1e-6, "micro"},
   651  	{1e-6, "μ"},
   652  	{1e-3, "milli"},
   653  	{1e-2, "centi"},
   654  	{1e-1, "deci"},
   655  	{1e1, "deka"},
   656  	{1e2, "hecta"},
   657  	{1e2, "hecto"},
   658  	{1e3, "kilo"},
   659  	{1e6, "mega"},
   660  	{1e6, "meg"},
   661  	{1e9, "giga"},
   662  	{1e12, "tera"},
   663  	{1e15, "peta"},
   664  	{1e18, "exa"},
   665  	{1e21, "zetta"},
   666  	{1e24, "yotta"},
   667  }
   668  
   669  func pname() float64 {
   670  	var i, j, n int
   671  	var s string
   672  
   673  	/*
   674  	 * rip off normal prefixs
   675  	 */
   676  	n = len(sym)
   677  	for i = 0; i < len(prefix); i++ {
   678  		s = prefix[i].name
   679  		j = len(s)
   680  		if j < n && sym[0:j] == s {
   681  			sym = sym[j:n]
   682  			return prefix[i].vval
   683  		}
   684  	}
   685  
   686  	/*
   687  	 * rip off 's' suffixes
   688  	 */
   689  	if n > 2 && sym[n-1] == 's' {
   690  		sym = sym[0 : n-1]
   691  		return 1
   692  	}
   693  
   694  	return 0
   695  }
   696  
   697  // careful multiplication
   698  // exponents (log) are checked before multiply
   699  func fmul(a, b float64) float64 {
   700  	var l float64
   701  
   702  	if b <= 0 {
   703  		if b == 0 {
   704  			return 0
   705  		}
   706  		l = math.Log(-b)
   707  	} else {
   708  		l = math.Log(b)
   709  	}
   710  
   711  	if a <= 0 {
   712  		if a == 0 {
   713  			return 0
   714  		}
   715  		l += math.Log(-a)
   716  	} else {
   717  		l += math.Log(a)
   718  	}
   719  
   720  	if l > Maxe {
   721  		Error("overflow in multiply")
   722  		return 1
   723  	}
   724  	if l < -Maxe {
   725  		Error("underflow in multiply")
   726  		return 0
   727  	}
   728  	return a * b
   729  }
   730  
   731  // careful division
   732  // exponents (log) are checked before divide
   733  func fdiv(a, b float64) float64 {
   734  	var l float64
   735  
   736  	if b <= 0 {
   737  		if b == 0 {
   738  			Errorf("division by zero: %v %v", a, b)
   739  			return 1
   740  		}
   741  		l = math.Log(-b)
   742  	} else {
   743  		l = math.Log(b)
   744  	}
   745  
   746  	if a <= 0 {
   747  		if a == 0 {
   748  			return 0
   749  		}
   750  		l -= math.Log(-a)
   751  	} else {
   752  		l -= math.Log(a)
   753  	}
   754  
   755  	if l < -Maxe {
   756  		Error("overflow in divide")
   757  		return 1
   758  	}
   759  	if l > Maxe {
   760  		Error("underflow in divide")
   761  		return 0
   762  	}
   763  	return a / b
   764  }
   765  
   766  func fadd(a, b float64) float64 {
   767  	return a + b
   768  }