go.talks: add OSCON "Gophers with Hammers" talk

LGTM=adg
R=adg, campoy, dvyukov
CC=golang-codereviews
https://golang.org/cl/113430043
diff --git a/2014/hammers.slide b/2014/hammers.slide
new file mode 100644
index 0000000..5758da1
--- /dev/null
+++ b/2014/hammers.slide
@@ -0,0 +1,398 @@
+Gophers With Hammers
+
+Josh Bleecher Snyder
+PayPal
+josharian@gmail.com
+@offbymany
+
+
+* Go was designed with tools in mind. (Rob Pike)
+
+* Designed with tools in mind
+
+Simple, regular syntax
+Simple semantics
+Batteries included
+
+* Tools everywhere
+
+- go
+- gofmt, goimports
+- godoc
+- go test [-cover] [-race]
+- go vet
+- gofix, gofmt -r, eg
+- oracle
+- golint
+- godep
+
+and more...
+
+* go command
+
+	$ go list -f '{{.Deps}}' bytes
+	[errors io runtime sync sync/atomic unicode unicode/utf8 unsafe]
+
+* gofmt
+
+from
+
+	for{
+	fmt.Println(      "I feel pretty." );
+	       }
+
+to
+
+	for {
+		fmt.Println("I feel pretty.")
+	}
+
+* godoc
+
+	$ godoc strings Repeat
+	func Repeat(s string, count int) string
+	    Repeat returns a new string consisting of count copies of the string s.
+
+* go vet
+
+Oops
+
+	if suffix != ".md" || suffix != ".markdown" {
+
+Flagged
+
+	suspect or: suffix != ".md" || suffix != ".markdown"
+
+* go tool cover -mode=set
+
+	func Repeat(s string, count int) string {
+		b := make([]byte, len(s)*count)
+		bp := 0
+		for i := 0; i < count; i++ {
+			bp += copy(b[bp:], s)
+		}
+		return string(b)
+	}
+
+to
+
+	func Repeat(s string, count int) string {
+		GoCover.Count[0] = 1
+		b := make([]byte, len(s)*count)
+		bp := 0
+		for i := 0; i < count; i++ {
+			GoCover.Count[2] = 1
+			bp += copy(b[bp:], s)
+		}
+		GoCover.Count[1] = 1
+		return string(b)
+	}
+
+* go test -cover
+
+	$ go test -coverprofile=c.out strings
+	ok  	strings	0.455s	coverage: 96.9% of statements
+
+	$ go tool cover -func=c.out
+	strings/reader.go:	Len				66.7%
+	strings/reader.go:	Read				100.0%
+	strings/reader.go:	ReadAt				100.0%
+	strings/reader.go:	ReadByte			100.0%
+	strings/reader.go:	UnreadByte			100.0%
+	strings/reader.go:	ReadRune			100.0%
+	strings/reader.go:	UnreadRune			100.0%
+	strings/reader.go:	Seek				90.9%
+	strings/reader.go:	WriteTo				83.3%
+	...
+	
+	$ go tool cover -html=c.out
+	# opens a browser window, shows line-by-line coverage
+
+
+* Tools to make tools
+
+- text/template
+- go/build
+- go/doc
+- go/format
+- go/{parser,token,ast,printer}
+- go.tools/go/types and friends
+
+and more...
+
+* Hammers are fun!
+
+# Why to write your own tools: Fun, learning, profit
+
+* impl
+
+Generate implementation stubs given an interface.
+
+	go get github.com/josharian/impl
+
+Generate
+
+	$ impl 'f *File' io.Reader
+	func (f *File) Read(p []byte) (n int, err error) {
+	}
+
+from
+
+	package io
+
+	type Reader interface {
+		Read(p []byte) (n int, err error)
+	}
+
+* impl
+
+Generate
+
+	$ impl 'f *File' io.ReadWriter
+	func (f *File) Read(p []byte) (n int, err error) {
+	}
+
+	func (f *File) Write(p []byte) (n int, err error) {
+	}
+
+from
+
+	package io
+
+	type ReadWriter interface {
+		Reader
+		Writer
+	}
+
+* impl
+
+Generate
+
+	$ impl 'c *Ctx' http.Handler
+	func (c *Ctx) ServeHTTP(http.ResponseWriter, *http.Request) {
+	}
+
+from
+
+	package http
+
+	type Handler interface {
+		ServeHTTP(ResponseWriter, *Request)
+	}
+
+* Plan
+
+*Find*import*path*and*interface*name*
+
+	http.Handler ⇒ net/http, Handler
+
+Parse interface
+
+	net/http, Handler ⇒ {{"ServeHTTP", {{"", "http.ResponseWriter"}, {"", "*http.Request"}}, {}}}}
+
+Generate output
+
+	{{"ServeHTTP", {{"", "http.ResponseWriter"}, {"", "*http.Request"}}, {}}}} ⇒ profit!
+
+* goimports ftw
+
+	import "code.google.com/p/go.tools/imports"
+
+.play hammers/importpath.go /func main/,/^}/
+
+* Hello, AST
+
+    *ast.File {
+    .  Package: 1:1
+    .  Name: *ast.Ident {
+    .  .  NamePos: 1:9
+    .  .  Name: "hack"
+    .  }
+    .  Decls: []ast.Decl (len = 2) {
+    .  .  0: *ast.GenDecl {
+    .  .  .  TokPos: 1:15
+    .  .  .  Tok: import
+    .  .  .  Lparen: -
+    .  .  .  Specs: []ast.Spec (len = 1) {
+    .  .  .  .  0: *ast.ImportSpec {
+    .  .  .  .  .  Path: *ast.BasicLit {
+    .  .  .  .  .  .  ValuePos: 1:22
+    .  .  .  .  .  .  Kind: STRING
+    .  .  .  .  .  .  Value: "\"net/http\""
+    .  .  .  .  .  }
+
+[truncated]
+
+
+* Extract the import path
+
+	import (
+		"go/parser"
+		"go/token"
+	)
+
+.play hammers/extractpath.go /func main/,/^}/
+
+* Extract the interface name
+
+	import "go/ast"
+
+.play hammers/extractiface.go /func main/,/^}/
+
+A `GenDecl` can have many `Specs`
+
+	var (
+		r io.Reader
+		w io.Writer
+	)
+
+* Plan
+
+Find import path and interface name
+
+	http.Handler ⇒ net/http, Handler
+
+*Parse*interface*
+
+	net/http, Handler ⇒ {{"ServeHTTP", {{"", "http.ResponseWriter"}, {"", "*http.Request"}}, {}}}}
+
+Generate output
+
+	{{"ServeHTTP", {{"", "http.ResponseWriter"}, {"", "*http.Request"}}, {}}}} ⇒ profit!
+
+* Data structures
+
+Represent
+
+	Read(p []byte) (n int, err error)
+
+as
+
+	Func{
+		Name:   "Read",
+		Params: []Param{{Name: "p", Type: "[]byte"}},
+		Res: []Param{
+			{Name: "n", Type: "int"},
+			{Name: "err", Type: "error"},
+		},
+	},
+
+* Data structures
+
+.code hammers/types.go /type Func/,/^}/
+.code hammers/types.go /type Param/,/^}/
+
+* Find the code
+
+	import "go/build"
+
+.play hammers/findthecode.go /func main/,/^}/
+
+* Find the interface declaration
+
+	import "go/printer"
+
+.play hammers/findtheifacedecl.go /func main/,/^}/
+
+* Extract function names
+
+No name? It's an embedded interface. Recurse.
+
+	type ByteScanner interface {
+	    ByteReader
+	    UnreadByte() error
+	}
+
+* Extract params and results
+
+No name? Just use `""`.
+
+	type ByteWriter interface {
+	    WriteByte(c byte) error
+	}
+
+* Qualify types
+
+Types can be arbitrarily complicated.
+
+	type CrazyGopher interface {
+		CrazyGoph(int) func(chan<- [32]byte, map[string]int64) ([]rune, error)
+	}
+
+And we need to rewrite some of them.
+
+	int ⇒ int
+	*Request ⇒ *http.Request
+	io.Reader ⇒ io.Reader
+	func(io.Reader, chan map[S][]*T) ⇒ func(io.Reader, chan map[foo.S][]*foo.T))
+
+* Qualify types
+
+.play hammers/fulltype.go /func main/,/end main/
+
+* Plan
+
+Find import path and interface name
+
+	http.Handler ⇒ net/http, Handler
+
+Parse interface
+
+	net/http, Handler ⇒ {{"ServeHTTP", {{"", "http.ResponseWriter"}, {"", "*http.Request"}}, {}}}}
+
+*Generate*output*
+
+	{{"ServeHTTP", {{"", "http.ResponseWriter"}, {"", "*http.Request"}}, {}}}} ⇒ profit!
+
+* Method type
+
+.code hammers/types.go /type Method/,/^}/
+.code hammers/types.go /type Func/,/^}/
+.code hammers/types.go /type Param/,/^}/
+
+* Use text/template
+
+.play hammers/codegen.go /func main/,/^}/
+
+# Don't generate an AST. It's a lot of work, and Go is its own DSL.
+
+* Ugly is ok
+
+	import "go/format"
+
+.play hammers/format.go /func main/,/^}/
+
+* Great success
+
+Full code plus tests at `github.com/josharian/impl`
+
+* Tips
+
+Use `go`get`-d` to download lots of code from `godoc.org/-/index`. (Don't forget to set a temporary `GOPATH`!)
+
+Use (and improve) `github.com/yuroyoro/goast-viewer`.
+
+You don't have to generate all the code. And generating data is even better.
+
+The `go/ast` docs are your friend.
+
+`go.tools/go/types` is powerful.
+
+`go`generate` is coming.
+
+
+* Nails!
+
+- Break up long strings
+- Enums and flags to Stringers
+- Dynamic code analysis
+- Vet checks
+- Reflect ⇒ codegen
+- Convention-based http dispatch
+- Detect "last line" copy/paste bugs
+- AST-aware diff, merge, blame; automated fork analysis
+- Machine learning models of ASTs: anomaly detection, bug-prone code detection
+- Code fingerprinting
+- Examine usage patterns
+- Compiler stress tests
+
diff --git a/2014/hammers/codegen.go b/2014/hammers/codegen.go
new file mode 100644
index 0000000..e0180cc
--- /dev/null
+++ b/2014/hammers/codegen.go
@@ -0,0 +1,45 @@
+// +build ignore
+
+package main
+
+import (
+	"os"
+	"text/template"
+)
+
+func main() {
+	const stub = "func ({{.Recv}}) {{.Name}}" +
+		"({{range .Params}}{{.Name}} {{.Type}}, {{end}})" +
+		"({{range .Res}}{{.Name}} {{.Type}}, {{end}})" +
+		"{\n}\n\n"
+	tmpl := template.Must(template.New("test").Parse(stub))
+
+	m := Method{
+		Recv: "f *File",
+		Func: Func{
+			Name: "Close",
+			Res:  []Param{{Type: "error"}},
+		},
+	}
+
+	tmpl.Execute(os.Stdout, m)
+}
+
+// Method represents a method signature.
+type Method struct {
+	Recv string
+	Func
+}
+
+// Func represents a function signature.
+type Func struct {
+	Name   string
+	Params []Param
+	Res    []Param
+}
+
+// Param represents a parameter in a function or method signature.
+type Param struct {
+	Name string
+	Type string
+}
diff --git a/2014/hammers/extractiface.go b/2014/hammers/extractiface.go
new file mode 100644
index 0000000..9086205
--- /dev/null
+++ b/2014/hammers/extractiface.go
@@ -0,0 +1,21 @@
+// +build ignore
+
+package main
+
+import (
+	"fmt"
+	"go/ast"
+	"go/parser"
+	"go/token"
+)
+
+func main() {
+	src := `package hack; import "net/http"; var i http.Handler`
+	f, _ := parser.ParseFile(token.NewFileSet(), "", src, 0)
+
+	decl := f.Decls[1].(*ast.GenDecl)      // var i http.Handler
+	spec := decl.Specs[0].(*ast.ValueSpec) // i http.Handler
+	sel := spec.Type.(*ast.SelectorExpr)   // http.Handler
+	id := sel.Sel.Name                     // Handler
+	fmt.Println(id)
+}
diff --git a/2014/hammers/extractpath.go b/2014/hammers/extractpath.go
new file mode 100644
index 0000000..d8002bc
--- /dev/null
+++ b/2014/hammers/extractpath.go
@@ -0,0 +1,21 @@
+// +build ignore
+
+package main
+
+import (
+	"fmt"
+	"go/parser"
+	"go/token"
+	"strconv"
+)
+
+func main() {
+	src := `package hack; import "net/http"; var i http.Handler`
+
+	fset := token.NewFileSet()
+	f, _ := parser.ParseFile(fset, "", src, 0)
+
+	raw := f.Imports[0].Path.Value
+	path, _ := strconv.Unquote(raw)
+	fmt.Println(raw, "\n", path)
+}
diff --git a/2014/hammers/findthecode.go b/2014/hammers/findthecode.go
new file mode 100644
index 0000000..95b12ac
--- /dev/null
+++ b/2014/hammers/findthecode.go
@@ -0,0 +1,14 @@
+// +build ignore
+
+package main
+
+import (
+	"fmt"
+	"go/build"
+)
+
+func main() {
+	pkg, _ := build.Import("net/http", "", 0) // HL
+	fmt.Println(pkg.Dir)
+	fmt.Println(pkg.GoFiles)
+}
diff --git a/2014/hammers/findtheifacedecl.go b/2014/hammers/findtheifacedecl.go
new file mode 100644
index 0000000..15cfcf6
--- /dev/null
+++ b/2014/hammers/findtheifacedecl.go
@@ -0,0 +1,51 @@
+// +build ignore
+
+package main
+
+import (
+	"go/ast"
+	"go/build"
+	"go/parser"
+	"go/printer"
+	"go/token"
+	"os"
+	"path/filepath"
+)
+
+func main() {
+	fset, files := parsePackage("net/http")
+	id := "Handler"
+
+	for _, f := range files {
+		for _, decl := range f.Decls {
+			decl, ok := decl.(*ast.GenDecl)
+			if !ok || decl.Tok != token.TYPE {
+				continue
+			}
+			for _, spec := range decl.Specs {
+				spec := spec.(*ast.TypeSpec)
+				if spec.Name.Name == id {
+					printer.Fprint(os.Stdout, fset, spec) // HL
+				}
+			}
+		}
+	}
+}
+
+func parsePackage(path string) (*token.FileSet, []*ast.File) {
+	pkg, err := build.Import(path, "", 0)
+	if err != nil {
+		panic(err)
+	}
+
+	fset := token.NewFileSet()
+	var files []*ast.File
+	for _, file := range pkg.GoFiles {
+		f, err := parser.ParseFile(fset, filepath.Join(pkg.Dir, file), nil, 0)
+		if err != nil {
+			continue
+		}
+		files = append(files, f)
+	}
+	return fset, files
+}
diff --git a/2014/hammers/format.go b/2014/hammers/format.go
new file mode 100644
index 0000000..45e5d49
--- /dev/null
+++ b/2014/hammers/format.go
@@ -0,0 +1,15 @@
+// +build ignore
+
+package main
+
+import (
+	"fmt"
+	"go/format"
+)
+
+func main() {
+	ugly := `func (f *File) Read(p []byte, )(n int, err error, ){}`
+	fmt.Println(ugly)
+	pretty, _ := format.Source([]byte(ugly)) // HL
+	fmt.Println(string(pretty))
+}
diff --git a/2014/hammers/fulltype.go b/2014/hammers/fulltype.go
new file mode 100644
index 0000000..80d4ffd
--- /dev/null
+++ b/2014/hammers/fulltype.go
@@ -0,0 +1,32 @@
+// +build ignore
+
+package main
+
+import (
+	"fmt"
+	"go/ast"
+	"go/parser"
+	"go/token"
+)
+
+func main() {
+	src := `
+package http
+type Handler interface {
+	ServeHTTP(ResponseWriter, *Request)
+}
+`
+	f, _ := parser.ParseFile(token.NewFileSet(), "", src, 0)
+	typ := f.Decls[0].(*ast.GenDecl).Specs[0].(*ast.TypeSpec).Type.(*ast.InterfaceType)
+	fndecl := typ.Methods.List[0].Type.(*ast.FuncType)
+	// fndecl: (ResponseWriter, *Request)
+
+	ast.Inspect(fndecl, func(n ast.Node) bool { // HL
+		if ident, ok := n.(*ast.Ident); ok {
+			fmt.Println(ident.Name)
+		}
+		return true
+	})
+}
+
+// end main // OMIT
diff --git a/2014/hammers/importpath.go b/2014/hammers/importpath.go
new file mode 100644
index 0000000..cee9787
--- /dev/null
+++ b/2014/hammers/importpath.go
@@ -0,0 +1,19 @@
+// +build ignore
+
+package main
+
+import (
+	"fmt"
+
+	"code.google.com/p/go.tools/imports"
+)
+
+func main() {
+	iface := "http.Handler"
+	src := "package hack; var i " + iface // HL
+	fmt.Println(src, "\n---")
+
+	imp, _ := imports.Process("", []byte(src), nil) // HL
+	// ignoring errors throughout this presentation
+	fmt.Println(string(imp))
+}
diff --git a/2014/hammers/types.go b/2014/hammers/types.go
new file mode 100644
index 0000000..f7aef4c
--- /dev/null
+++ b/2014/hammers/types.go
@@ -0,0 +1,22 @@
+// +build ignore
+
+package main
+
+// Method represents a method signature.
+type Method struct {
+	Recv string
+	Func
+}
+
+// Func represents a function signature.
+type Func struct {
+	Name   string
+	Params []Param
+	Res    []Param
+}
+
+// Param represents a parameter in a function or method signature.
+type Param struct {
+	Name string
+	Type string
+}