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 }