mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-12-18 19:54:21 +08:00
deploy: 06a1bdf735
This commit is contained in:
90
vendor/gopl.io/ch12/display/display.go
generated
vendored
Normal file
90
vendor/gopl.io/ch12/display/display.go
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 333.
|
||||
|
||||
// Package display provides a means to display structured data.
|
||||
package display
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
//!+Display
|
||||
|
||||
func Display(name string, x interface{}) {
|
||||
fmt.Printf("Display %s (%T):\n", name, x)
|
||||
display(name, reflect.ValueOf(x))
|
||||
}
|
||||
|
||||
//!-Display
|
||||
|
||||
// formatAtom formats a value without inspecting its internal structure.
|
||||
// It is a copy of the the function in gopl.io/ch11/format.
|
||||
func formatAtom(v reflect.Value) string {
|
||||
switch v.Kind() {
|
||||
case reflect.Invalid:
|
||||
return "invalid"
|
||||
case reflect.Int, reflect.Int8, reflect.Int16,
|
||||
reflect.Int32, reflect.Int64:
|
||||
return strconv.FormatInt(v.Int(), 10)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16,
|
||||
reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return strconv.FormatUint(v.Uint(), 10)
|
||||
// ...floating-point and complex cases omitted for brevity...
|
||||
case reflect.Bool:
|
||||
if v.Bool() {
|
||||
return "true"
|
||||
}
|
||||
return "false"
|
||||
case reflect.String:
|
||||
return strconv.Quote(v.String())
|
||||
case reflect.Chan, reflect.Func, reflect.Ptr,
|
||||
reflect.Slice, reflect.Map:
|
||||
return v.Type().String() + " 0x" +
|
||||
strconv.FormatUint(uint64(v.Pointer()), 16)
|
||||
default: // reflect.Array, reflect.Struct, reflect.Interface
|
||||
return v.Type().String() + " value"
|
||||
}
|
||||
}
|
||||
|
||||
//!+display
|
||||
func display(path string, v reflect.Value) {
|
||||
switch v.Kind() {
|
||||
case reflect.Invalid:
|
||||
fmt.Printf("%s = invalid\n", path)
|
||||
case reflect.Slice, reflect.Array:
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
display(fmt.Sprintf("%s[%d]", path, i), v.Index(i))
|
||||
}
|
||||
case reflect.Struct:
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
fieldPath := fmt.Sprintf("%s.%s", path, v.Type().Field(i).Name)
|
||||
display(fieldPath, v.Field(i))
|
||||
}
|
||||
case reflect.Map:
|
||||
for _, key := range v.MapKeys() {
|
||||
display(fmt.Sprintf("%s[%s]", path,
|
||||
formatAtom(key)), v.MapIndex(key))
|
||||
}
|
||||
case reflect.Ptr:
|
||||
if v.IsNil() {
|
||||
fmt.Printf("%s = nil\n", path)
|
||||
} else {
|
||||
display(fmt.Sprintf("(*%s)", path), v.Elem())
|
||||
}
|
||||
case reflect.Interface:
|
||||
if v.IsNil() {
|
||||
fmt.Printf("%s = nil\n", path)
|
||||
} else {
|
||||
fmt.Printf("%s.type = %s\n", path, v.Elem().Type())
|
||||
display(path+".value", v.Elem())
|
||||
}
|
||||
default: // basic types, channels, funcs
|
||||
fmt.Printf("%s = %s\n", path, formatAtom(v))
|
||||
}
|
||||
}
|
||||
|
||||
//!-display
|
||||
259
vendor/gopl.io/ch12/display/display_test.go
generated
vendored
Normal file
259
vendor/gopl.io/ch12/display/display_test.go
generated
vendored
Normal file
@@ -0,0 +1,259 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
package display
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"gopl.io/ch7/eval"
|
||||
)
|
||||
|
||||
// NOTE: we can't use !+..!- comments to excerpt these tests
|
||||
// into the book because it defeats the Example mechanism,
|
||||
// which requires the // Output comment to be at the end
|
||||
// of the function.
|
||||
|
||||
func Example_expr() {
|
||||
e, _ := eval.Parse("sqrt(A / pi)")
|
||||
Display("e", e)
|
||||
// Output:
|
||||
// Display e (eval.call):
|
||||
// e.fn = "sqrt"
|
||||
// e.args[0].type = eval.binary
|
||||
// e.args[0].value.op = 47
|
||||
// e.args[0].value.x.type = eval.Var
|
||||
// e.args[0].value.x.value = "A"
|
||||
// e.args[0].value.y.type = eval.Var
|
||||
// e.args[0].value.y.value = "pi"
|
||||
}
|
||||
|
||||
func Example_slice() {
|
||||
Display("slice", []*int{new(int), nil})
|
||||
// Output:
|
||||
// Display slice ([]*int):
|
||||
// (*slice[0]) = 0
|
||||
// slice[1] = nil
|
||||
}
|
||||
|
||||
func Example_nilInterface() {
|
||||
var w io.Writer
|
||||
Display("w", w)
|
||||
// Output:
|
||||
// Display w (<nil>):
|
||||
// w = invalid
|
||||
}
|
||||
|
||||
func Example_ptrToInterface() {
|
||||
var w io.Writer
|
||||
Display("&w", &w)
|
||||
// Output:
|
||||
// Display &w (*io.Writer):
|
||||
// (*&w) = nil
|
||||
}
|
||||
|
||||
func Example_struct() {
|
||||
Display("x", struct{ x interface{} }{3})
|
||||
// Output:
|
||||
// Display x (struct { x interface {} }):
|
||||
// x.x.type = int
|
||||
// x.x.value = 3
|
||||
}
|
||||
|
||||
func Example_interface() {
|
||||
var i interface{} = 3
|
||||
Display("i", i)
|
||||
// Output:
|
||||
// Display i (int):
|
||||
// i = 3
|
||||
}
|
||||
|
||||
func Example_ptrToInterface2() {
|
||||
var i interface{} = 3
|
||||
Display("&i", &i)
|
||||
// Output:
|
||||
// Display &i (*interface {}):
|
||||
// (*&i).type = int
|
||||
// (*&i).value = 3
|
||||
}
|
||||
|
||||
func Example_array() {
|
||||
Display("x", [1]interface{}{3})
|
||||
// Output:
|
||||
// Display x ([1]interface {}):
|
||||
// x[0].type = int
|
||||
// x[0].value = 3
|
||||
}
|
||||
|
||||
func Example_movie() {
|
||||
//!+movie
|
||||
type Movie struct {
|
||||
Title, Subtitle string
|
||||
Year int
|
||||
Color bool
|
||||
Actor map[string]string
|
||||
Oscars []string
|
||||
Sequel *string
|
||||
}
|
||||
//!-movie
|
||||
//!+strangelove
|
||||
strangelove := Movie{
|
||||
Title: "Dr. Strangelove",
|
||||
Subtitle: "How I Learned to Stop Worrying and Love the Bomb",
|
||||
Year: 1964,
|
||||
Color: false,
|
||||
Actor: map[string]string{
|
||||
"Dr. Strangelove": "Peter Sellers",
|
||||
"Grp. Capt. Lionel Mandrake": "Peter Sellers",
|
||||
"Pres. Merkin Muffley": "Peter Sellers",
|
||||
"Gen. Buck Turgidson": "George C. Scott",
|
||||
"Brig. Gen. Jack D. Ripper": "Sterling Hayden",
|
||||
`Maj. T.J. "King" Kong`: "Slim Pickens",
|
||||
},
|
||||
|
||||
Oscars: []string{
|
||||
"Best Actor (Nomin.)",
|
||||
"Best Adapted Screenplay (Nomin.)",
|
||||
"Best Director (Nomin.)",
|
||||
"Best Picture (Nomin.)",
|
||||
},
|
||||
}
|
||||
//!-strangelove
|
||||
Display("strangelove", strangelove)
|
||||
|
||||
// We don't use an Output: comment since displaying
|
||||
// a map is nondeterministic.
|
||||
/*
|
||||
//!+output
|
||||
Display strangelove (display.Movie):
|
||||
strangelove.Title = "Dr. Strangelove"
|
||||
strangelove.Subtitle = "How I Learned to Stop Worrying and Love the Bomb"
|
||||
strangelove.Year = 1964
|
||||
strangelove.Color = false
|
||||
strangelove.Actor["Gen. Buck Turgidson"] = "George C. Scott"
|
||||
strangelove.Actor["Brig. Gen. Jack D. Ripper"] = "Sterling Hayden"
|
||||
strangelove.Actor["Maj. T.J. \"King\" Kong"] = "Slim Pickens"
|
||||
strangelove.Actor["Dr. Strangelove"] = "Peter Sellers"
|
||||
strangelove.Actor["Grp. Capt. Lionel Mandrake"] = "Peter Sellers"
|
||||
strangelove.Actor["Pres. Merkin Muffley"] = "Peter Sellers"
|
||||
strangelove.Oscars[0] = "Best Actor (Nomin.)"
|
||||
strangelove.Oscars[1] = "Best Adapted Screenplay (Nomin.)"
|
||||
strangelove.Oscars[2] = "Best Director (Nomin.)"
|
||||
strangelove.Oscars[3] = "Best Picture (Nomin.)"
|
||||
strangelove.Sequel = nil
|
||||
//!-output
|
||||
*/
|
||||
}
|
||||
|
||||
// This test ensures that the program terminates without crashing.
|
||||
func Test(t *testing.T) {
|
||||
// Some other values (YMMV)
|
||||
Display("os.Stderr", os.Stderr)
|
||||
// Output:
|
||||
// Display os.Stderr (*os.File):
|
||||
// (*(*os.Stderr).file).fd = 2
|
||||
// (*(*os.Stderr).file).name = "/dev/stderr"
|
||||
// (*(*os.Stderr).file).nepipe = 0
|
||||
|
||||
var w io.Writer = os.Stderr
|
||||
Display("&w", &w)
|
||||
// Output:
|
||||
// Display &w (*io.Writer):
|
||||
// (*&w).type = *os.File
|
||||
// (*(*(*&w).value).file).fd = 2
|
||||
// (*(*(*&w).value).file).name = "/dev/stderr"
|
||||
// (*(*(*&w).value).file).nepipe = 0
|
||||
|
||||
var locker sync.Locker = new(sync.Mutex)
|
||||
Display("(&locker)", &locker)
|
||||
// Output:
|
||||
// Display (&locker) (*sync.Locker):
|
||||
// (*(&locker)).type = *sync.Mutex
|
||||
// (*(*(&locker)).value).state = 0
|
||||
// (*(*(&locker)).value).sema = 0
|
||||
|
||||
Display("locker", locker)
|
||||
// Output:
|
||||
// Display locker (*sync.Mutex):
|
||||
// (*locker).state = 0
|
||||
// (*locker).sema = 0
|
||||
// (*(&locker)) = nil
|
||||
|
||||
locker = nil
|
||||
Display("(&locker)", &locker)
|
||||
// Output:
|
||||
// Display (&locker) (*sync.Locker):
|
||||
// (*(&locker)) = nil
|
||||
|
||||
ips, _ := net.LookupHost("golang.org")
|
||||
Display("ips", ips)
|
||||
// Output:
|
||||
// Display ips ([]string):
|
||||
// ips[0] = "173.194.68.141"
|
||||
// ips[1] = "2607:f8b0:400d:c06::8d"
|
||||
|
||||
// Even metarecursion! (YMMV)
|
||||
Display("rV", reflect.ValueOf(os.Stderr))
|
||||
// Output:
|
||||
// Display rV (reflect.Value):
|
||||
// (*rV.typ).size = 8
|
||||
// (*rV.typ).ptrdata = 8
|
||||
// (*rV.typ).hash = 871609668
|
||||
// (*rV.typ)._ = 0
|
||||
// ...
|
||||
|
||||
// a pointer that points to itself
|
||||
type P *P
|
||||
var p P
|
||||
p = &p
|
||||
if false {
|
||||
Display("p", p)
|
||||
// Output:
|
||||
// Display p (display.P):
|
||||
// ...stuck, no output...
|
||||
}
|
||||
|
||||
// a map that contains itself
|
||||
type M map[string]M
|
||||
m := make(M)
|
||||
m[""] = m
|
||||
if false {
|
||||
Display("m", m)
|
||||
// Output:
|
||||
// Display m (display.M):
|
||||
// ...stuck, no output...
|
||||
}
|
||||
|
||||
// a slice that contains itself
|
||||
type S []S
|
||||
s := make(S, 1)
|
||||
s[0] = s
|
||||
if false {
|
||||
Display("s", s)
|
||||
// Output:
|
||||
// Display s (display.S):
|
||||
// ...stuck, no output...
|
||||
}
|
||||
|
||||
// a linked list that eats its own tail
|
||||
type Cycle struct {
|
||||
Value int
|
||||
Tail *Cycle
|
||||
}
|
||||
var c Cycle
|
||||
c = Cycle{42, &c}
|
||||
if false {
|
||||
Display("c", c)
|
||||
// Output:
|
||||
// Display c (display.Cycle):
|
||||
// c.Value = 42
|
||||
// (*c.Tail).Value = 42
|
||||
// (*(*c.Tail).Tail).Value = 42
|
||||
// ...ad infinitum...
|
||||
}
|
||||
}
|
||||
44
vendor/gopl.io/ch12/format/format.go
generated
vendored
Normal file
44
vendor/gopl.io/ch12/format/format.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 332.
|
||||
|
||||
// Package format provides an Any function that can format any value.
|
||||
//!+
|
||||
package format
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Any formats any value as a string.
|
||||
func Any(value interface{}) string {
|
||||
return formatAtom(reflect.ValueOf(value))
|
||||
}
|
||||
|
||||
// formatAtom formats a value without inspecting its internal structure.
|
||||
func formatAtom(v reflect.Value) string {
|
||||
switch v.Kind() {
|
||||
case reflect.Invalid:
|
||||
return "invalid"
|
||||
case reflect.Int, reflect.Int8, reflect.Int16,
|
||||
reflect.Int32, reflect.Int64:
|
||||
return strconv.FormatInt(v.Int(), 10)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16,
|
||||
reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return strconv.FormatUint(v.Uint(), 10)
|
||||
// ...floating-point and complex cases omitted for brevity...
|
||||
case reflect.Bool:
|
||||
return strconv.FormatBool(v.Bool())
|
||||
case reflect.String:
|
||||
return strconv.Quote(v.String())
|
||||
case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Slice, reflect.Map:
|
||||
return v.Type().String() + " 0x" +
|
||||
strconv.FormatUint(uint64(v.Pointer()), 16)
|
||||
default: // reflect.Array, reflect.Struct, reflect.Interface
|
||||
return v.Type().String() + " value"
|
||||
}
|
||||
}
|
||||
|
||||
//!-
|
||||
24
vendor/gopl.io/ch12/format/format_test.go
generated
vendored
Normal file
24
vendor/gopl.io/ch12/format/format_test.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
package format_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gopl.io/ch12/format"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
// The pointer values are just examples, and may vary from run to run.
|
||||
//!+time
|
||||
var x int64 = 1
|
||||
var d time.Duration = 1 * time.Nanosecond
|
||||
fmt.Println(format.Any(x)) // "1"
|
||||
fmt.Println(format.Any(d)) // "1"
|
||||
fmt.Println(format.Any([]int64{x})) // "[]int64 0x8202b87b0"
|
||||
fmt.Println(format.Any([]time.Duration{d})) // "[]time.Duration 0x8202b87e0"
|
||||
//!-time
|
||||
}
|
||||
29
vendor/gopl.io/ch12/methods/methods.go
generated
vendored
Normal file
29
vendor/gopl.io/ch12/methods/methods.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 351.
|
||||
|
||||
// Package methods provides a function to print the methods of any value.
|
||||
package methods
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//!+print
|
||||
// Print prints the method set of the value x.
|
||||
func Print(x interface{}) {
|
||||
v := reflect.ValueOf(x)
|
||||
t := v.Type()
|
||||
fmt.Printf("type %s\n", t)
|
||||
|
||||
for i := 0; i < v.NumMethod(); i++ {
|
||||
methType := v.Method(i).Type()
|
||||
fmt.Printf("func (%s) %s%s\n", t, t.Method(i).Name,
|
||||
strings.TrimPrefix(methType.String(), "func"))
|
||||
}
|
||||
}
|
||||
|
||||
//!-print
|
||||
49
vendor/gopl.io/ch12/methods/methods_test.go
generated
vendored
Normal file
49
vendor/gopl.io/ch12/methods/methods_test.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
package methods_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gopl.io/ch12/methods"
|
||||
)
|
||||
|
||||
func ExamplePrintDuration() {
|
||||
methods.Print(time.Hour)
|
||||
// Output:
|
||||
// type time.Duration
|
||||
// func (time.Duration) Hours() float64
|
||||
// func (time.Duration) Minutes() float64
|
||||
// func (time.Duration) Nanoseconds() int64
|
||||
// func (time.Duration) Seconds() float64
|
||||
// func (time.Duration) String() string
|
||||
}
|
||||
|
||||
func ExamplePrintReplacer() {
|
||||
methods.Print(new(strings.Replacer))
|
||||
// Output:
|
||||
// type *strings.Replacer
|
||||
// func (*strings.Replacer) Replace(string) string
|
||||
// func (*strings.Replacer) WriteString(io.Writer, string) (int, error)
|
||||
}
|
||||
|
||||
/*
|
||||
//!+output
|
||||
methods.Print(time.Hour)
|
||||
// Output:
|
||||
// type time.Duration
|
||||
// func (time.Duration) Hours() float64
|
||||
// func (time.Duration) Minutes() float64
|
||||
// func (time.Duration) Nanoseconds() int64
|
||||
// func (time.Duration) Seconds() float64
|
||||
// func (time.Duration) String() string
|
||||
|
||||
methods.Print(new(strings.Replacer))
|
||||
// Output:
|
||||
// type *strings.Replacer
|
||||
// func (*strings.Replacer) Replace(string) string
|
||||
// func (*strings.Replacer) WriteString(io.Writer, string) (int, error)
|
||||
//!-output
|
||||
*/
|
||||
90
vendor/gopl.io/ch12/params/params.go
generated
vendored
Normal file
90
vendor/gopl.io/ch12/params/params.go
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 349.
|
||||
|
||||
// Package params provides a reflection-based parser for URL parameters.
|
||||
package params
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//!+Unpack
|
||||
|
||||
// Unpack populates the fields of the struct pointed to by ptr
|
||||
// from the HTTP request parameters in req.
|
||||
func Unpack(req *http.Request, ptr interface{}) error {
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Build map of fields keyed by effective name.
|
||||
fields := make(map[string]reflect.Value)
|
||||
v := reflect.ValueOf(ptr).Elem() // the struct variable
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
fieldInfo := v.Type().Field(i) // a reflect.StructField
|
||||
tag := fieldInfo.Tag // a reflect.StructTag
|
||||
name := tag.Get("http")
|
||||
if name == "" {
|
||||
name = strings.ToLower(fieldInfo.Name)
|
||||
}
|
||||
fields[name] = v.Field(i)
|
||||
}
|
||||
|
||||
// Update struct field for each parameter in the request.
|
||||
for name, values := range req.Form {
|
||||
f := fields[name]
|
||||
if !f.IsValid() {
|
||||
continue // ignore unrecognized HTTP parameters
|
||||
}
|
||||
for _, value := range values {
|
||||
if f.Kind() == reflect.Slice {
|
||||
elem := reflect.New(f.Type().Elem()).Elem()
|
||||
if err := populate(elem, value); err != nil {
|
||||
return fmt.Errorf("%s: %v", name, err)
|
||||
}
|
||||
f.Set(reflect.Append(f, elem))
|
||||
} else {
|
||||
if err := populate(f, value); err != nil {
|
||||
return fmt.Errorf("%s: %v", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//!-Unpack
|
||||
|
||||
//!+populate
|
||||
func populate(v reflect.Value, value string) error {
|
||||
switch v.Kind() {
|
||||
case reflect.String:
|
||||
v.SetString(value)
|
||||
|
||||
case reflect.Int:
|
||||
i, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.SetInt(i)
|
||||
|
||||
case reflect.Bool:
|
||||
b, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.SetBool(b)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported kind %s", v.Type())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//!-populate
|
||||
60
vendor/gopl.io/ch12/search/main.go
generated
vendored
Normal file
60
vendor/gopl.io/ch12/search/main.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 348.
|
||||
|
||||
// Search is a demo of the params.Unpack function.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
//!+
|
||||
|
||||
import "gopl.io/ch12/params"
|
||||
|
||||
// search implements the /search URL endpoint.
|
||||
func search(resp http.ResponseWriter, req *http.Request) {
|
||||
var data struct {
|
||||
Labels []string `http:"l"`
|
||||
MaxResults int `http:"max"`
|
||||
Exact bool `http:"x"`
|
||||
}
|
||||
data.MaxResults = 10 // set default
|
||||
if err := params.Unpack(req, &data); err != nil {
|
||||
http.Error(resp, err.Error(), http.StatusBadRequest) // 400
|
||||
return
|
||||
}
|
||||
|
||||
// ...rest of handler...
|
||||
fmt.Fprintf(resp, "Search: %+v\n", data)
|
||||
}
|
||||
|
||||
//!-
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/search", search)
|
||||
log.Fatal(http.ListenAndServe(":12345", nil))
|
||||
}
|
||||
|
||||
/*
|
||||
//!+output
|
||||
$ go build gopl.io/ch12/search
|
||||
$ ./search &
|
||||
$ ./fetch 'http://localhost:12345/search'
|
||||
Search: {Labels:[] MaxResults:10 Exact:false}
|
||||
$ ./fetch 'http://localhost:12345/search?l=golang&l=programming'
|
||||
Search: {Labels:[golang programming] MaxResults:10 Exact:false}
|
||||
$ ./fetch 'http://localhost:12345/search?l=golang&l=programming&max=100'
|
||||
Search: {Labels:[golang programming] MaxResults:100 Exact:false}
|
||||
$ ./fetch 'http://localhost:12345/search?x=true&l=golang&l=programming'
|
||||
Search: {Labels:[golang programming] MaxResults:10 Exact:true}
|
||||
$ ./fetch 'http://localhost:12345/search?q=hello&x=123'
|
||||
x: strconv.ParseBool: parsing "123": invalid syntax
|
||||
$ ./fetch 'http://localhost:12345/search?q=hello&max=lots'
|
||||
max: strconv.ParseInt: parsing "lots": invalid syntax
|
||||
//!-output
|
||||
*/
|
||||
162
vendor/gopl.io/ch12/sexpr/decode.go
generated
vendored
Normal file
162
vendor/gopl.io/ch12/sexpr/decode.go
generated
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 344.
|
||||
|
||||
// Package sexpr provides a means for converting Go objects to and
|
||||
// from S-expressions.
|
||||
package sexpr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"text/scanner"
|
||||
)
|
||||
|
||||
//!+Unmarshal
|
||||
// Unmarshal parses S-expression data and populates the variable
|
||||
// whose address is in the non-nil pointer out.
|
||||
func Unmarshal(data []byte, out interface{}) (err error) {
|
||||
lex := &lexer{scan: scanner.Scanner{Mode: scanner.GoTokens}}
|
||||
lex.scan.Init(bytes.NewReader(data))
|
||||
lex.next() // get the first token
|
||||
defer func() {
|
||||
// NOTE: this is not an example of ideal error handling.
|
||||
if x := recover(); x != nil {
|
||||
err = fmt.Errorf("error at %s: %v", lex.scan.Position, x)
|
||||
}
|
||||
}()
|
||||
read(lex, reflect.ValueOf(out).Elem())
|
||||
return nil
|
||||
}
|
||||
|
||||
//!-Unmarshal
|
||||
|
||||
//!+lexer
|
||||
type lexer struct {
|
||||
scan scanner.Scanner
|
||||
token rune // the current token
|
||||
}
|
||||
|
||||
func (lex *lexer) next() { lex.token = lex.scan.Scan() }
|
||||
func (lex *lexer) text() string { return lex.scan.TokenText() }
|
||||
|
||||
func (lex *lexer) consume(want rune) {
|
||||
if lex.token != want { // NOTE: Not an example of good error handling.
|
||||
panic(fmt.Sprintf("got %q, want %q", lex.text(), want))
|
||||
}
|
||||
lex.next()
|
||||
}
|
||||
|
||||
//!-lexer
|
||||
|
||||
// The read function is a decoder for a small subset of well-formed
|
||||
// S-expressions. For brevity of our example, it takes many dubious
|
||||
// shortcuts.
|
||||
//
|
||||
// The parser assumes
|
||||
// - that the S-expression input is well-formed; it does no error checking.
|
||||
// - that the S-expression input corresponds to the type of the variable.
|
||||
// - that all numbers in the input are non-negative decimal integers.
|
||||
// - that all keys in ((key value) ...) struct syntax are unquoted symbols.
|
||||
// - that the input does not contain dotted lists such as (1 2 . 3).
|
||||
// - that the input does not contain Lisp reader macros such 'x and #'x.
|
||||
//
|
||||
// The reflection logic assumes
|
||||
// - that v is always a variable of the appropriate type for the
|
||||
// S-expression value. For example, v must not be a boolean,
|
||||
// interface, channel, or function, and if v is an array, the input
|
||||
// must have the correct number of elements.
|
||||
// - that v in the top-level call to read has the zero value of its
|
||||
// type and doesn't need clearing.
|
||||
// - that if v is a numeric variable, it is a signed integer.
|
||||
|
||||
//!+read
|
||||
func read(lex *lexer, v reflect.Value) {
|
||||
switch lex.token {
|
||||
case scanner.Ident:
|
||||
// The only valid identifiers are
|
||||
// "nil" and struct field names.
|
||||
if lex.text() == "nil" {
|
||||
v.Set(reflect.Zero(v.Type()))
|
||||
lex.next()
|
||||
return
|
||||
}
|
||||
case scanner.String:
|
||||
s, _ := strconv.Unquote(lex.text()) // NOTE: ignoring errors
|
||||
v.SetString(s)
|
||||
lex.next()
|
||||
return
|
||||
case scanner.Int:
|
||||
i, _ := strconv.Atoi(lex.text()) // NOTE: ignoring errors
|
||||
v.SetInt(int64(i))
|
||||
lex.next()
|
||||
return
|
||||
case '(':
|
||||
lex.next()
|
||||
readList(lex, v)
|
||||
lex.next() // consume ')'
|
||||
return
|
||||
}
|
||||
panic(fmt.Sprintf("unexpected token %q", lex.text()))
|
||||
}
|
||||
|
||||
//!-read
|
||||
|
||||
//!+readlist
|
||||
func readList(lex *lexer, v reflect.Value) {
|
||||
switch v.Kind() {
|
||||
case reflect.Array: // (item ...)
|
||||
for i := 0; !endList(lex); i++ {
|
||||
read(lex, v.Index(i))
|
||||
}
|
||||
|
||||
case reflect.Slice: // (item ...)
|
||||
for !endList(lex) {
|
||||
item := reflect.New(v.Type().Elem()).Elem()
|
||||
read(lex, item)
|
||||
v.Set(reflect.Append(v, item))
|
||||
}
|
||||
|
||||
case reflect.Struct: // ((name value) ...)
|
||||
for !endList(lex) {
|
||||
lex.consume('(')
|
||||
if lex.token != scanner.Ident {
|
||||
panic(fmt.Sprintf("got token %q, want field name", lex.text()))
|
||||
}
|
||||
name := lex.text()
|
||||
lex.next()
|
||||
read(lex, v.FieldByName(name))
|
||||
lex.consume(')')
|
||||
}
|
||||
|
||||
case reflect.Map: // ((key value) ...)
|
||||
v.Set(reflect.MakeMap(v.Type()))
|
||||
for !endList(lex) {
|
||||
lex.consume('(')
|
||||
key := reflect.New(v.Type().Key()).Elem()
|
||||
read(lex, key)
|
||||
value := reflect.New(v.Type().Elem()).Elem()
|
||||
read(lex, value)
|
||||
v.SetMapIndex(key, value)
|
||||
lex.consume(')')
|
||||
}
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("cannot decode list into %v", v.Type()))
|
||||
}
|
||||
}
|
||||
|
||||
func endList(lex *lexer) bool {
|
||||
switch lex.token {
|
||||
case scanner.EOF:
|
||||
panic("end of file")
|
||||
case ')':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//!-readlist
|
||||
97
vendor/gopl.io/ch12/sexpr/encode.go
generated
vendored
Normal file
97
vendor/gopl.io/ch12/sexpr/encode.go
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 339.
|
||||
|
||||
package sexpr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
//!+Marshal
|
||||
// Marshal encodes a Go value in S-expression form.
|
||||
func Marshal(v interface{}) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
if err := encode(&buf, reflect.ValueOf(v)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
//!-Marshal
|
||||
|
||||
// encode writes to buf an S-expression representation of v.
|
||||
//!+encode
|
||||
func encode(buf *bytes.Buffer, v reflect.Value) error {
|
||||
switch v.Kind() {
|
||||
case reflect.Invalid:
|
||||
buf.WriteString("nil")
|
||||
|
||||
case reflect.Int, reflect.Int8, reflect.Int16,
|
||||
reflect.Int32, reflect.Int64:
|
||||
fmt.Fprintf(buf, "%d", v.Int())
|
||||
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16,
|
||||
reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
fmt.Fprintf(buf, "%d", v.Uint())
|
||||
|
||||
case reflect.String:
|
||||
fmt.Fprintf(buf, "%q", v.String())
|
||||
|
||||
case reflect.Ptr:
|
||||
return encode(buf, v.Elem())
|
||||
|
||||
case reflect.Array, reflect.Slice: // (value ...)
|
||||
buf.WriteByte('(')
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if i > 0 {
|
||||
buf.WriteByte(' ')
|
||||
}
|
||||
if err := encode(buf, v.Index(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
buf.WriteByte(')')
|
||||
|
||||
case reflect.Struct: // ((name value) ...)
|
||||
buf.WriteByte('(')
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
if i > 0 {
|
||||
buf.WriteByte(' ')
|
||||
}
|
||||
fmt.Fprintf(buf, "(%s ", v.Type().Field(i).Name)
|
||||
if err := encode(buf, v.Field(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
buf.WriteByte(')')
|
||||
}
|
||||
buf.WriteByte(')')
|
||||
|
||||
case reflect.Map: // ((key value) ...)
|
||||
buf.WriteByte('(')
|
||||
for i, key := range v.MapKeys() {
|
||||
if i > 0 {
|
||||
buf.WriteByte(' ')
|
||||
}
|
||||
buf.WriteByte('(')
|
||||
if err := encode(buf, key); err != nil {
|
||||
return err
|
||||
}
|
||||
buf.WriteByte(' ')
|
||||
if err := encode(buf, v.MapIndex(key)); err != nil {
|
||||
return err
|
||||
}
|
||||
buf.WriteByte(')')
|
||||
}
|
||||
buf.WriteByte(')')
|
||||
|
||||
default: // float, complex, bool, chan, func, interface
|
||||
return fmt.Errorf("unsupported type: %s", v.Type())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//!-encode
|
||||
183
vendor/gopl.io/ch12/sexpr/pretty.go
generated
vendored
Normal file
183
vendor/gopl.io/ch12/sexpr/pretty.go
generated
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
package sexpr
|
||||
|
||||
// This file implements the algorithm described in Derek C. Oppen's
|
||||
// 1979 Stanford technical report, "Pretty Printing".
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func MarshalIndent(v interface{}) ([]byte, error) {
|
||||
p := printer{width: margin}
|
||||
if err := pretty(&p, reflect.ValueOf(v)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p.Bytes(), nil
|
||||
}
|
||||
|
||||
const margin = 80
|
||||
|
||||
type token struct {
|
||||
kind rune // one of "s ()" (string, blank, start, end)
|
||||
str string
|
||||
size int
|
||||
}
|
||||
|
||||
type printer struct {
|
||||
tokens []*token // FIFO buffer
|
||||
stack []*token // stack of open ' ' and '(' tokens
|
||||
rtotal int // total number of spaces needed to print stream
|
||||
|
||||
bytes.Buffer
|
||||
indents []int
|
||||
width int // remaining space
|
||||
}
|
||||
|
||||
func (p *printer) string(str string) {
|
||||
tok := &token{kind: 's', str: str, size: len(str)}
|
||||
if len(p.stack) == 0 {
|
||||
p.print(tok)
|
||||
} else {
|
||||
p.tokens = append(p.tokens, tok)
|
||||
p.rtotal += len(str)
|
||||
}
|
||||
}
|
||||
func (p *printer) pop() (top *token) {
|
||||
last := len(p.stack) - 1
|
||||
top, p.stack = p.stack[last], p.stack[:last]
|
||||
return
|
||||
}
|
||||
func (p *printer) begin() {
|
||||
if len(p.stack) == 0 {
|
||||
p.rtotal = 1
|
||||
}
|
||||
t := &token{kind: '(', size: -p.rtotal}
|
||||
p.tokens = append(p.tokens, t)
|
||||
p.stack = append(p.stack, t) // push
|
||||
p.string("(")
|
||||
}
|
||||
func (p *printer) end() {
|
||||
p.string(")")
|
||||
p.tokens = append(p.tokens, &token{kind: ')'})
|
||||
x := p.pop()
|
||||
x.size += p.rtotal
|
||||
if x.kind == ' ' {
|
||||
p.pop().size += p.rtotal
|
||||
}
|
||||
if len(p.stack) == 0 {
|
||||
for _, tok := range p.tokens {
|
||||
p.print(tok)
|
||||
}
|
||||
p.tokens = nil
|
||||
}
|
||||
}
|
||||
func (p *printer) space() {
|
||||
last := len(p.stack) - 1
|
||||
x := p.stack[last]
|
||||
if x.kind == ' ' {
|
||||
x.size += p.rtotal
|
||||
p.stack = p.stack[:last] // pop
|
||||
}
|
||||
t := &token{kind: ' ', size: -p.rtotal}
|
||||
p.tokens = append(p.tokens, t)
|
||||
p.stack = append(p.stack, t)
|
||||
p.rtotal++
|
||||
}
|
||||
func (p *printer) print(t *token) {
|
||||
switch t.kind {
|
||||
case 's':
|
||||
p.WriteString(t.str)
|
||||
p.width -= len(t.str)
|
||||
case '(':
|
||||
p.indents = append(p.indents, p.width)
|
||||
case ')':
|
||||
p.indents = p.indents[:len(p.indents)-1] // pop
|
||||
case ' ':
|
||||
if t.size > p.width {
|
||||
p.width = p.indents[len(p.indents)-1] - 1
|
||||
fmt.Fprintf(&p.Buffer, "\n%*s", margin-p.width, "")
|
||||
} else {
|
||||
p.WriteByte(' ')
|
||||
p.width--
|
||||
}
|
||||
}
|
||||
}
|
||||
func (p *printer) stringf(format string, args ...interface{}) {
|
||||
p.string(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func pretty(p *printer, v reflect.Value) error {
|
||||
switch v.Kind() {
|
||||
case reflect.Invalid:
|
||||
p.string("nil")
|
||||
|
||||
case reflect.Int, reflect.Int8, reflect.Int16,
|
||||
reflect.Int32, reflect.Int64:
|
||||
p.stringf("%d", v.Int())
|
||||
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16,
|
||||
reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
p.stringf("%d", v.Uint())
|
||||
|
||||
case reflect.String:
|
||||
p.stringf("%q", v.String())
|
||||
|
||||
case reflect.Array, reflect.Slice: // (value ...)
|
||||
p.begin()
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if i > 0 {
|
||||
p.space()
|
||||
}
|
||||
if err := pretty(p, v.Index(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
p.end()
|
||||
|
||||
case reflect.Struct: // ((name value ...)
|
||||
p.begin()
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
if i > 0 {
|
||||
p.space()
|
||||
}
|
||||
p.begin()
|
||||
p.string(v.Type().Field(i).Name)
|
||||
p.space()
|
||||
if err := pretty(p, v.Field(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
p.end()
|
||||
}
|
||||
p.end()
|
||||
|
||||
case reflect.Map: // ((key value ...)
|
||||
p.begin()
|
||||
for i, key := range v.MapKeys() {
|
||||
if i > 0 {
|
||||
p.space()
|
||||
}
|
||||
p.begin()
|
||||
if err := pretty(p, key); err != nil {
|
||||
return err
|
||||
}
|
||||
p.space()
|
||||
if err := pretty(p, v.MapIndex(key)); err != nil {
|
||||
return err
|
||||
}
|
||||
p.end()
|
||||
}
|
||||
p.end()
|
||||
|
||||
case reflect.Ptr:
|
||||
return pretty(p, v.Elem())
|
||||
|
||||
default: // float, complex, bool, chan, func, interface
|
||||
return fmt.Errorf("unsupported type: %s", v.Type())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
74
vendor/gopl.io/ch12/sexpr/sexpr_test.go
generated
vendored
Normal file
74
vendor/gopl.io/ch12/sexpr/sexpr_test.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
package sexpr
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test verifies that encoding and decoding a complex data value
|
||||
// produces an equal result.
|
||||
//
|
||||
// The test does not make direct assertions about the encoded output
|
||||
// because the output depends on map iteration order, which is
|
||||
// nondeterministic. The output of the t.Log statements can be
|
||||
// inspected by running the test with the -v flag:
|
||||
//
|
||||
// $ go test -v gopl.io/ch12/sexpr
|
||||
//
|
||||
func Test(t *testing.T) {
|
||||
type Movie struct {
|
||||
Title, Subtitle string
|
||||
Year int
|
||||
Actor map[string]string
|
||||
Oscars []string
|
||||
Sequel *string
|
||||
}
|
||||
strangelove := Movie{
|
||||
Title: "Dr. Strangelove",
|
||||
Subtitle: "How I Learned to Stop Worrying and Love the Bomb",
|
||||
Year: 1964,
|
||||
Actor: map[string]string{
|
||||
"Dr. Strangelove": "Peter Sellers",
|
||||
"Grp. Capt. Lionel Mandrake": "Peter Sellers",
|
||||
"Pres. Merkin Muffley": "Peter Sellers",
|
||||
"Gen. Buck Turgidson": "George C. Scott",
|
||||
"Brig. Gen. Jack D. Ripper": "Sterling Hayden",
|
||||
`Maj. T.J. "King" Kong`: "Slim Pickens",
|
||||
},
|
||||
Oscars: []string{
|
||||
"Best Actor (Nomin.)",
|
||||
"Best Adapted Screenplay (Nomin.)",
|
||||
"Best Director (Nomin.)",
|
||||
"Best Picture (Nomin.)",
|
||||
},
|
||||
}
|
||||
|
||||
// Encode it
|
||||
data, err := Marshal(strangelove)
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal failed: %v", err)
|
||||
}
|
||||
t.Logf("Marshal() = %s\n", data)
|
||||
|
||||
// Decode it
|
||||
var movie Movie
|
||||
if err := Unmarshal(data, &movie); err != nil {
|
||||
t.Fatalf("Unmarshal failed: %v", err)
|
||||
}
|
||||
t.Logf("Unmarshal() = %+v\n", movie)
|
||||
|
||||
// Check equality.
|
||||
if !reflect.DeepEqual(movie, strangelove) {
|
||||
t.Fatal("not equal")
|
||||
}
|
||||
|
||||
// Pretty-print it:
|
||||
data, err = MarshalIndent(strangelove)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("MarshalIdent() = %s\n", data)
|
||||
}
|
||||
Reference in New Issue
Block a user