good good study, day day up!

This commit is contained in:
chai2010
2015-12-09 15:45:11 +08:00
commit 1693baf5de
378 changed files with 23276 additions and 0 deletions

40
vendor/gopl.io/ch7/eval/ast.go generated vendored Normal file
View File

@@ -0,0 +1,40 @@
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
package eval
// An Expr is an arithmetic expression.
type Expr interface {
// Eval returns the value of this Expr in the environment env.
Eval(env Env) float64
// Check reports errors in this Expr and adds its Vars to the set.
Check(vars map[Var]bool) error
}
//!+ast
// A Var identifies a variable, e.g., x.
type Var string
// A literal is a numeric constant, e.g., 3.141.
type literal float64
// A unary represents a unary operator expression, e.g., -x.
type unary struct {
op rune // one of '+', '-'
x Expr
}
// A binary represents a binary operator expression, e.g., x+y.
type binary struct {
op rune // one of '+', '-', '*', '/'
x, y Expr
}
// A call represents a function call expression, e.g., sin(x).
type call struct {
fn string // one of "pow", "sin", "sqrt"
args []Expr
}
//!-ast

58
vendor/gopl.io/ch7/eval/check.go generated vendored Normal file
View File

@@ -0,0 +1,58 @@
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
package eval
import (
"fmt"
"strings"
)
//!+Check
func (v Var) Check(vars map[Var]bool) error {
vars[v] = true
return nil
}
func (literal) Check(vars map[Var]bool) error {
return nil
}
func (u unary) Check(vars map[Var]bool) error {
if !strings.ContainsRune("+-", u.op) {
return fmt.Errorf("unexpected unary op %q", u.op)
}
return u.x.Check(vars)
}
func (b binary) Check(vars map[Var]bool) error {
if !strings.ContainsRune("+-*/", b.op) {
return fmt.Errorf("unexpected binary op %q", b.op)
}
if err := b.x.Check(vars); err != nil {
return err
}
return b.y.Check(vars)
}
func (c call) Check(vars map[Var]bool) error {
arity, ok := numParams[c.fn]
if !ok {
return fmt.Errorf("unknown function %q", c.fn)
}
if len(c.args) != arity {
return fmt.Errorf("call to %s has %d args, want %d",
c.fn, len(c.args), arity)
}
for _, arg := range c.args {
if err := arg.Check(vars); err != nil {
return err
}
}
return nil
}
var numParams = map[string]int{"pow": 2, "sin": 1, "sqrt": 1}
//!-Check

48
vendor/gopl.io/ch7/eval/coverage_test.go generated vendored Normal file
View File

@@ -0,0 +1,48 @@
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
package eval
import (
"fmt"
"math"
"testing"
)
//!+TestCoverage
func TestCoverage(t *testing.T) {
var tests = []struct {
input string
env Env
want string // expected error from Parse/Check or result from Eval
}{
{"x % 2", nil, "unexpected '%'"},
{"!true", nil, "unexpected '!'"},
{"log(10)", nil, `unknown function "log"`},
{"sqrt(1, 2)", nil, "call to sqrt has 2 args, want 1"},
{"sqrt(A / pi)", Env{"A": 87616, "pi": math.Pi}, "167"},
{"pow(x, 3) + pow(y, 3)", Env{"x": 9, "y": 10}, "1729"},
{"5 / 9 * (F - 32)", Env{"F": -40}, "-40"},
}
for _, test := range tests {
expr, err := Parse(test.input)
if err == nil {
err = expr.Check(map[Var]bool{})
}
if err != nil {
if err.Error() != test.want {
t.Errorf("%s: got %q, want %q", test.input, err, test.want)
}
continue
}
got := fmt.Sprintf("%.6g", expr.Eval(test.env))
if got != test.want {
t.Errorf("%s: %v => %s, want %s",
test.input, test.env, got, test.want)
}
}
}
//!-TestCoverage

70
vendor/gopl.io/ch7/eval/eval.go generated vendored Normal file
View File

@@ -0,0 +1,70 @@
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
// See page 198.
// Package eval provides an expression evaluator.
package eval
import (
"fmt"
"math"
)
//!+env
type Env map[Var]float64
//!-env
//!+Eval1
func (v Var) Eval(env Env) float64 {
return env[v]
}
func (l literal) Eval(_ Env) float64 {
return float64(l)
}
//!-Eval1
//!+Eval2
func (u unary) Eval(env Env) float64 {
switch u.op {
case '+':
return +u.x.Eval(env)
case '-':
return -u.x.Eval(env)
}
panic(fmt.Sprintf("unsupported unary operator: %q", u.op))
}
func (b binary) Eval(env Env) float64 {
switch b.op {
case '+':
return b.x.Eval(env) + b.y.Eval(env)
case '-':
return b.x.Eval(env) - b.y.Eval(env)
case '*':
return b.x.Eval(env) * b.y.Eval(env)
case '/':
return b.x.Eval(env) / b.y.Eval(env)
}
panic(fmt.Sprintf("unsupported binary operator: %q", b.op))
}
func (c call) Eval(env Env) float64 {
switch c.fn {
case "pow":
return math.Pow(c.args[0].Eval(env), c.args[1].Eval(env))
case "sin":
return math.Sin(c.args[0].Eval(env))
case "sqrt":
return math.Sqrt(c.args[0].Eval(env))
}
panic(fmt.Sprintf("unsupported function call: %s", c.fn))
}
//!-Eval2

113
vendor/gopl.io/ch7/eval/eval_test.go generated vendored Normal file
View File

@@ -0,0 +1,113 @@
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
package eval
import (
"fmt"
"math"
"testing"
)
//!+Eval
func TestEval(t *testing.T) {
tests := []struct {
expr string
env Env
want string
}{
{"sqrt(A / pi)", Env{"A": 87616, "pi": math.Pi}, "167"},
{"pow(x, 3) + pow(y, 3)", Env{"x": 12, "y": 1}, "1729"},
{"pow(x, 3) + pow(y, 3)", Env{"x": 9, "y": 10}, "1729"},
{"5 / 9 * (F - 32)", Env{"F": -40}, "-40"},
{"5 / 9 * (F - 32)", Env{"F": 32}, "0"},
{"5 / 9 * (F - 32)", Env{"F": 212}, "100"},
//!-Eval
// additional tests that don't appear in the book
{"-1 + -x", Env{"x": 1}, "-2"},
{"-1 - x", Env{"x": 1}, "-2"},
//!+Eval
}
var prevExpr string
for _, test := range tests {
// Print expr only when it changes.
if test.expr != prevExpr {
fmt.Printf("\n%s\n", test.expr)
prevExpr = test.expr
}
expr, err := Parse(test.expr)
if err != nil {
t.Error(err) // parse error
continue
}
got := fmt.Sprintf("%.6g", expr.Eval(test.env))
fmt.Printf("\t%v => %s\n", test.env, got)
if got != test.want {
t.Errorf("%s.Eval() in %v = %q, want %q\n",
test.expr, test.env, got, test.want)
}
}
}
//!-Eval
/*
//!+output
sqrt(A / pi)
map[A:87616 pi:3.141592653589793] => 167
pow(x, 3) + pow(y, 3)
map[x:12 y:1] => 1729
map[x:9 y:10] => 1729
5 / 9 * (F - 32)
map[F:-40] => -40
map[F:32] => 0
map[F:212] => 100
//!-output
// Additional outputs that don't appear in the book.
-1 - x
map[x:1] => -2
-1 + -x
map[x:1] => -2
*/
func TestErrors(t *testing.T) {
for _, test := range []struct{ expr, wantErr string }{
{"x % 2", "unexpected '%'"},
{"math.Pi", "unexpected '.'"},
{"!true", "unexpected '!'"},
{`"hello"`, "unexpected '\"'"},
{"log(10)", `unknown function "log"`},
{"sqrt(1, 2)", "call to sqrt has 2 args, want 1"},
} {
expr, err := Parse(test.expr)
if err == nil {
vars := make(map[Var]bool)
err = expr.Check(vars)
if err == nil {
t.Errorf("unexpected success: %s", test.expr)
continue
}
}
fmt.Printf("%-20s%v\n", test.expr, err) // (for book)
if err.Error() != test.wantErr {
t.Errorf("got error %s, want %s", err, test.wantErr)
}
}
}
/*
//!+errors
x % 2 unexpected '%'
math.Pi unexpected '.'
!true unexpected '!'
"hello" unexpected '"'
log(10) unknown function "log"
sqrt(1, 2) call to sqrt has 2 args, want 1
//!-errors
*/

160
vendor/gopl.io/ch7/eval/parse.go generated vendored Normal file
View File

@@ -0,0 +1,160 @@
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
package eval
import (
"fmt"
"strconv"
"strings"
"text/scanner"
)
// ---- lexer ----
// This lexer is similar to the one described in Chapter 13.
type lexer struct {
scan scanner.Scanner
token rune // current lookahead token
}
func (lex *lexer) next() { lex.token = lex.scan.Scan() }
func (lex *lexer) text() string { return lex.scan.TokenText() }
type lexPanic string
// describe returns a string describing the current token, for use in errors.
func (lex *lexer) describe() string {
switch lex.token {
case scanner.EOF:
return "end of file"
case scanner.Ident:
return fmt.Sprintf("identifier %s", lex.text())
case scanner.Int, scanner.Float:
return fmt.Sprintf("number %s", lex.text())
}
return fmt.Sprintf("%q", rune(lex.token)) // any other rune
}
func precedence(op rune) int {
switch op {
case '*', '/':
return 2
case '+', '-':
return 1
}
return 0
}
// ---- parser ----
// Parse parses the input string as an arithmetic expression.
//
// expr = num a literal number, e.g., 3.14159
// | id a variable name, e.g., x
// | id '(' expr ',' ... ')' a function call
// | '-' expr a unary operator (+-)
// | expr '+' expr a binary operator (+-*/)
//
func Parse(input string) (_ Expr, err error) {
defer func() {
switch x := recover().(type) {
case nil:
// no panic
case lexPanic:
err = fmt.Errorf("%s", x)
default:
// unexpected panic: resume state of panic.
panic(x)
}
}()
lex := new(lexer)
lex.scan.Init(strings.NewReader(input))
lex.scan.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanFloats
lex.next() // initial lookahead
e := parseExpr(lex)
if lex.token != scanner.EOF {
return nil, fmt.Errorf("unexpected %s", lex.describe())
}
return e, nil
}
func parseExpr(lex *lexer) Expr { return parseBinary(lex, 1) }
// binary = unary ('+' binary)*
// parseBinary stops when it encounters an
// operator of lower precedence than prec1.
func parseBinary(lex *lexer, prec1 int) Expr {
lhs := parseUnary(lex)
for prec := precedence(lex.token); prec >= prec1; prec-- {
for precedence(lex.token) == prec {
op := lex.token
lex.next() // consume operator
rhs := parseBinary(lex, prec+1)
lhs = binary{op, lhs, rhs}
}
}
return lhs
}
// unary = '+' expr | primary
func parseUnary(lex *lexer) Expr {
if lex.token == '+' || lex.token == '-' {
op := lex.token
lex.next() // consume '+' or '-'
return unary{op, parseUnary(lex)}
}
return parsePrimary(lex)
}
// primary = id
// | id '(' expr ',' ... ',' expr ')'
// | num
// | '(' expr ')'
func parsePrimary(lex *lexer) Expr {
switch lex.token {
case scanner.Ident:
id := lex.text()
lex.next() // consume Ident
if lex.token != '(' {
return Var(id)
}
lex.next() // consume '('
var args []Expr
if lex.token != ')' {
for {
args = append(args, parseExpr(lex))
if lex.token != ',' {
break
}
lex.next() // consume ','
}
if lex.token != ')' {
msg := fmt.Sprintf("got %q, want ')'", lex.token)
panic(lexPanic(msg))
}
}
lex.next() // consume ')'
return call{id, args}
case scanner.Int, scanner.Float:
f, err := strconv.ParseFloat(lex.text(), 64)
if err != nil {
panic(lexPanic(err.Error()))
}
lex.next() // consume number
return literal(f)
case '(':
lex.next() // consume ')'
e := parseExpr(lex)
if lex.token != ')' {
msg := fmt.Sprintf("got %s, want ')'", lex.describe())
panic(lexPanic(msg))
}
lex.next() // consume ')'
return e
}
msg := fmt.Sprintf("unexpected %s", lex.describe())
panic(lexPanic(msg))
}

52
vendor/gopl.io/ch7/eval/print.go generated vendored Normal file
View File

@@ -0,0 +1,52 @@
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
package eval
import (
"bytes"
"fmt"
)
// Format formats an expression as a string.
// It does not attempt to remove unnecessary parens.
func Format(e Expr) string {
var buf bytes.Buffer
write(&buf, e)
return buf.String()
}
func write(buf *bytes.Buffer, e Expr) {
switch e := e.(type) {
case literal:
fmt.Fprintf(buf, "%g", e)
case Var:
fmt.Fprintf(buf, "%s", e)
case unary:
fmt.Fprintf(buf, "(%c", e.op)
write(buf, e.x)
buf.WriteByte(')')
case binary:
buf.WriteByte('(')
write(buf, e.x)
fmt.Fprintf(buf, " %c ", e.op)
write(buf, e.y)
buf.WriteByte(')')
case call:
fmt.Fprintf(buf, "%s(", e.fn)
for i, arg := range e.args {
if i > 0 {
buf.WriteString(", ")
}
write(buf, arg)
}
buf.WriteByte(')')
default:
panic(fmt.Sprintf("unknown Expr: %T", e))
}
}