demo

package
v0.1.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 8, 2019 License: Apache-2.0 Imports: 4 Imported by: 0

Documentation

Overview

Package demo is used for demonstration and testing of walkabout.

Example (Abstract)

This example demonstrates how an enhanced visitable type can be used as though it were a tree of nodes.

package main

import (
	"fmt"
	"reflect"

	"github.com/cockroachdb/walkabout/demo"
)

func main() {
	data, _ := demo.NewContainer(true)

	for i := 0; i < data.TargetCount(); i++ {
		child := data.TargetAt(i)
		if child == nil {
			fmt.Printf("%d: nil\n", i)
		} else {
			fmt.Printf("%d: %s %s\n", i, child.TargetTypeID(), reflect.TypeOf(child))
		}
	}

}
Output:

0: ByRefType *demo.ByRefType
1: ByRefType *demo.ByRefType
2: []ByRefType *demo.targetAbstract
3: []*ByRefType *demo.targetAbstract
4: ByValType *demo.ByValType
5: ByValType *demo.ByValType
6: []ByValType *demo.targetAbstract
7: []*ByValType *demo.targetAbstract
8: nil
9: ByValType *demo.ByValType
10: ByValType *demo.ByValType
11: ByValType *demo.ByValType
12: ByValType *demo.ByValType
13: []Target *demo.targetAbstract
14: []*Target *demo.targetAbstract
15: []Target *demo.targetAbstract
Example (Actions)

This example shows a toy calculator AST and how custom actions can be introduced into the visitation flow. We've decided to use a visitor to stringify the Calculation. It's not how one would generally approach this problem, but it's useful to demonstrate more advanced flow-control.

package main

import (
	"fmt"
	"strconv"
	"strings"
)

// This generation flow will find all types in this package that
// are reachable from the Calculation struct and create a
// Calc interface to unify them.
//go:generate -command walkabout go run ..
//go:generate walkabout --union Calc --reachable Calculation

// This example shows a toy calculator AST and how custom actions can be
// introduced into the visitation flow. We've decided to use a visitor
// to stringify the Calculation. It's not how one would generally
// approach this problem, but it's useful to demonstrate more advanced
// flow-control.
func main() {
	c := &Calculation{
		Expr: &Func{"Avg", []Expr{
			&BinaryOp{"+", &Scalar{1}, &Scalar{3}},
			&Func{"Sum", []Expr{
				&Scalar{10},
				&Func{"Random", []Expr{&Scalar{1}, &Scalar{10}}},
				&Scalar{99}}},
			&BinaryOp{"*", &Scalar{5}, &Scalar{3}},
		}},
	}

	var w strings.Builder
	_, _, err := WalkCalc(c, func(ctx CalcContext, x Calc) CalcDecision {
		switch t := x.(type) {
		case *BinaryOp:
			// With a BinaryOp, we want to visit left, print the operator,
			// then right. We'll generate a sequence of actions to be
			// executed.
			return ctx.Actions(
				ctx.ActionVisit(t.Left),
				ctx.ActionCall(func() error { w.WriteString(t.Operator); return nil }),
				ctx.ActionVisit(t.Right),
			)

		case *Func:
			// With a function, we want to print commas between the arguments.
			// Rather than creating an arbitrarily number of actions to
			// perform, an Intercept() function can be registered to
			// traverse a struct normally and receive a pre-visit for
			// the immediate children. We combine Intercept() and Post()
			// to print the closing parenthesis.
			w.WriteString(t.Fn)
			w.WriteString("(")

			comma := ""
			return ctx.Continue().Intercept(func(CalcContext, Calc) (ret CalcDecision) {
				w.WriteString(comma)
				comma = ", "
				return
			}).Post(func(CalcContext, Calc) (ret CalcDecision) {
				w.WriteString(")")
				return
			})

		case *Scalar:
			w.WriteString(strconv.Itoa(t.val))
		}
		return ctx.Continue()
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(w.String())

}

type Calculation struct{ Expr Expr }

type Expr interface {
	Calc
	isExpr()
}

type BinaryOp struct {
	Operator string
	Left     Expr
	Right    Expr
}

func (*BinaryOp) isExpr() {}

type Scalar struct{ val int }

func (*Scalar) isExpr() {}

type Func struct {
	Fn   string
	Args []Expr
}

func (*Func) isExpr() {}
Output:

Avg(1+3, Sum(10, Random(1, 10), 99), 5*3)
Example (Error)

This example shows how an error can be returned from a visitor function.

package main

import (
	"errors"
	"fmt"

	"github.com/cockroachdb/walkabout/demo"
)

func main() {
	data, _ := demo.NewContainer(true)
	ret, changed, err := data.WalkTarget(
		func(ctx demo.TargetContext, x demo.Target) demo.TargetDecision {
			return ctx.Error(errors.New("an error"))
		})
	fmt.Println(ret, changed, err)

}
Output:

<nil> false an error
Example (Post)

This example demonstrates how pre- and post-visitation works. It also shows how visitation can be short-circuited.

package main

import (
	"fmt"

	"github.com/cockroachdb/walkabout/demo"
)

func main() {
	data, _ := demo.NewContainer(true)
	_, _, err := data.WalkTarget(func(ctx demo.TargetContext, x demo.Target) demo.TargetDecision {
		switch x.(type) {
		case *demo.ContainerType:
			fmt.Println("pre container")
			return ctx.Continue().Post(func(demo.TargetContext, demo.Target) (d demo.TargetDecision) {
				fmt.Println("post container")
				return
			})
		default:
			fmt.Println("halting")
			return ctx.Halt()
		}
	})
	if err != nil {
		panic(err)
	}

}
Output:

pre container
halting
post container
Example (Replace)

This example demonstrates how copy-on-replace can be implemented for "immutable" datastructures.

package main

import (
	"fmt"

	"github.com/cockroachdb/walkabout/demo"
)

func main() {
	data, _ := demo.NewContainer(true)
	count := 0
	data2, changed, err := data.WalkTarget(
		func(ctx demo.TargetContext, x demo.Target) demo.TargetDecision {
			switch t := x.(type) {
			case *demo.ByRefType:
				count++
				cp := *t
				cp.Val = fmt.Sprintf("ByRef %d", count)
				return ctx.Skip().Replace(&cp)

			case *demo.ByValType:
				count++
				cp := *t
				cp.Val = fmt.Sprintf("ByVal %d", count)
				return ctx.Skip().Replace(&cp)
			default:
				return ctx.Continue()
			}
		})
	if err != nil {
		panic(err)
	}
	fmt.Printf("Changed: %v\n", changed)
	fmt.Printf("data != data2: %v", data != data2)

}
Output:

Changed: true
data != data2: true
Example (Walk)

This example demonstrates how enhanced visitable types can be visited with a function.

package main

import (
	"fmt"
	"reflect"

	"github.com/cockroachdb/walkabout/demo"
)

func main() {
	data, _ := demo.NewContainer(true)
	var container, byVal, byRef int
	_, _, err := data.WalkTarget(func(ctx demo.TargetContext, x demo.Target) (d demo.TargetDecision) {
		switch x.(type) {
		case *demo.ContainerType:
			container++
		case *demo.ByValType:
			byVal++
		case *demo.ByRefType:
			byRef++
		default:
			return ctx.Error(fmt.Errorf("unknown type %s", reflect.TypeOf(x)))
		}
		return
	})
	if err != nil {
		panic(err)
	}
	fmt.Printf("Saw %d Container, %d ByValType, and %d ByRefType",
		container, byVal, byRef)
}
Output:

Saw 1 Container, 17 ByValType, and 6 ByRefType

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ByRefType

type ByRefType struct {
	Val string
}

ByRefType implements Target with a pointer receiver.

func (*ByRefType) TargetAt added in v0.1.4

func (x *ByRefType) TargetAt(index int) TargetAbstract

TargetAt implements TargetAbstract.

func (*ByRefType) TargetCount added in v0.1.4

func (x *ByRefType) TargetCount() int

TargetCount returns 0.

func (*ByRefType) TargetTypeID added in v0.1.4

func (*ByRefType) TargetTypeID() TargetTypeID

TargetTypeID returns TargetTypeByRefType.

func (*ByRefType) Value

func (x *ByRefType) Value() string

Value implements the Target interface.

func (*ByRefType) WalkTarget

func (x *ByRefType) WalkTarget(fn TargetWalkerFn) (_ *ByRefType, changed bool, err error)

WalkTarget visits the receiver with the provided callback.

type ByValType

type ByValType struct {
	Val string
}

ByValType implements the Target interface with a value receiver.

func (*ByValType) TargetAt added in v0.1.4

func (x *ByValType) TargetAt(index int) TargetAbstract

TargetAt implements TargetAbstract.

func (*ByValType) TargetCount added in v0.1.4

func (x *ByValType) TargetCount() int

TargetCount returns 0.

func (*ByValType) TargetTypeID added in v0.1.4

func (*ByValType) TargetTypeID() TargetTypeID

TargetTypeID returns TargetTypeByValType.

func (ByValType) Value

func (x ByValType) Value() string

Value implements the Target interface.

func (*ByValType) WalkTarget

func (x *ByValType) WalkTarget(fn TargetWalkerFn) (_ *ByValType, changed bool, err error)

WalkTarget visits the receiver with the provided callback.

type ContainerType

type ContainerType struct {
	ByRef         ByRefType
	ByRefPtr      *ByRefType
	ByRefSlice    []ByRefType
	ByRefPtrSlice []*ByRefType

	ByVal         ByValType
	ByValPtr      *ByValType
	ByValSlice    []ByValType
	ByValPtrSlice []*ByValType

	// We can break cycles, too.
	Container *ContainerType

	AnotherTarget    Target
	AnotherTargetPtr *Target

	// Interfaces which extend the visitable interface are supported.
	EmbedsTarget    EmbedsTarget
	EmbedsTargetPtr *EmbedsTarget

	// Slices of interfaces are supported.
	TargetSlice []Target

	// We can support slices of interface pointers.
	InterfacePtrSlice []*Target

	// Demonstrate use of named visitable type.
	NamedTargets Targets

	// Unexported types aren't generated.
	Ignored *ignoredType

	// This field will be generated only when in --union mode.
	UnionableType *UnionableType

	/// This field will only be visited when in --union --reachable mode.
	ReachableType ReachableType

	// This type is declared in another package. It shouldn't be present
	// in any configuration, unless we allow the code generator to
	// start writing to multiple directories, in which case we can make
	// --union --reachable work.
	OtherReachable other.Reachable

	// This field is in --reachable mode, since it does implement
	// our Target interface.
	OtherImplementor other.Implementor
	// contains filtered or unexported fields
}

ContainerType is just a regular struct that contains fields whose types implement or contain Target.

func NewContainer

func NewContainer(useValuePtrs bool) (*ContainerType, int)

NewContainer generates test data.

func (*ContainerType) TargetAt added in v0.1.4

func (x *ContainerType) TargetAt(index int) TargetAbstract

TargetAt implements TargetAbstract.

func (*ContainerType) TargetCount added in v0.1.4

func (x *ContainerType) TargetCount() int

TargetCount returns 16.

func (*ContainerType) TargetTypeID added in v0.1.4

func (*ContainerType) TargetTypeID() TargetTypeID

TargetTypeID returns TargetTypeContainerType.

func (*ContainerType) Value

func (*ContainerType) Value() string

Value implements the Target interface.

func (*ContainerType) WalkTarget

func (x *ContainerType) WalkTarget(fn TargetWalkerFn) (_ *ContainerType, changed bool, err error)

WalkTarget visits the receiver with the provided callback.

type EmbedsTarget

type EmbedsTarget interface {
	Target
	// contains filtered or unexported methods
}

EmbedsTarget demonstrates an interface hierarchy.

type NeverType

type NeverType struct{}

NeverType isn't reachable from any type that implements Target, so it will never be generated.

type ReachableType

type ReachableType struct{}

ReachableType will be included in --union --reachable mode. It doesn't implement the Target interface, but it is a field in ContainerType.

type Target

type Target interface {
	Value() string
}

Target is a base interface that we run the code-generator against. There's nothing special about this interface.

func WalkTarget

func WalkTarget(x Target, fn TargetWalkerFn) (_ Target, changed bool, err error)

WalkTarget visits the receiver with the provided callback.

type TargetAbstract

type TargetAbstract interface {
	// TargetAt returns the nth field of a struct or nth element of a
	// slice. If the child is a type which directly implements
	// TargetAbstract, it will be returned. If the child is of a pointer or
	// interface type, the value will be automatically dereferenced if it
	// is non-nil. If the child is a slice type, a TargetAbstract wrapper
	// around the slice will be returned.
	TargetAt(index int) TargetAbstract
	// TargetCount returns the number of visitable fields in a struct,
	// or the length of a slice.
	TargetCount() int
	// TargetTypeID returns a type token.
	TargetTypeID() TargetTypeID
}

TargetAbstract allows users to treat a Target as an abstract tree of nodes. All visitable struct types will have generated methods which implement this interface.

type TargetAction added in v0.1.4

type TargetAction e.Action

TargetAction is used by TargetContext.Actions() and allows users to have fine-grained control over traversal.

type TargetContext

type TargetContext struct {
	// contains filtered or unexported fields
}

TargetContext is provided to TargetWalkerFn and acts as a factory for constructing TargetDecision instances.

func (*TargetContext) ActionCall added in v0.1.4

func (c *TargetContext) ActionCall(fn func() error) TargetAction

ActionCall constructs a TargetAction that will invoke the given callback.

func (*TargetContext) ActionVisit added in v0.1.4

func (c *TargetContext) ActionVisit(x Target) TargetAction

ActionVisit constructs a TargetAction that will visit the given value.

func (*TargetContext) Actions added in v0.1.4

func (c *TargetContext) Actions(actions ...TargetAction) TargetDecision

Actions will perform the given actions in place of visiting values that would normally be visited. This allows callers to control specific field visitation order or to insert additional callbacks between visiting certain values.

func (*TargetContext) Continue

func (c *TargetContext) Continue() TargetDecision

Continue returns the zero-value of TargetDecision. It exists only for cases where it improves the readability of code.

func (*TargetContext) Error

func (c *TargetContext) Error(err error) TargetDecision

Error returns a TargetDecision which will cause the given error to be returned from the Walk() function. Post-visit functions will not be called.

func (*TargetContext) Halt

func (c *TargetContext) Halt() TargetDecision

Halt will end a visitation early and return from the Walk() function. Any registered post-visit functions will be called.

func (*TargetContext) Skip

func (c *TargetContext) Skip() TargetDecision

Skip will not traverse the fields of the current object.

type TargetDecision

type TargetDecision e.Decision

TargetDecision is used by TargetWalkerFn to control visitation. The TargetContext provided to a TargetWalkerFn acts as a factory for TargetDecision instances. In general, the factory methods choose a traversal strategy and additional methods on the TargetDecision can achieve a variety of side-effects.

func (TargetDecision) Intercept added in v0.1.4

Intercept registers a function to be called immediately before visiting each field or element of the current value.

func (TargetDecision) Post

Post registers a post-visit function, which will be called after the fields of the current object. The function can make another decision about the current value.

func (TargetDecision) Replace

func (d TargetDecision) Replace(x Target) TargetDecision

Replace allows the currently-visited value to be replaced. All parent nodes will be cloned.

type TargetTypeID added in v0.1.4

type TargetTypeID e.TypeID

TargetTypeID is a lightweight type token.

const (
	TargetTypeByRefType TargetTypeID
	TargetTypeByRefTypePtr
	TargetTypeByRefTypePtrSlice
	TargetTypeByRefTypeSlice
	TargetTypeByValType
	TargetTypeByValTypePtr
	TargetTypeByValTypePtrSlice
	TargetTypeByValTypeSlice
	TargetTypeContainerType
	TargetTypeContainerTypePtr
	TargetTypeEmbedsTarget
	TargetTypeEmbedsTargetPtr
	TargetTypeTarget
	TargetTypeTargetPtr
	TargetTypeTargetPtrSlice
	TargetTypeTargetSlice
)

These are lightweight type tokens.

func (TargetTypeID) String added in v0.1.4

func (t TargetTypeID) String() string

String is for debugging use only.

type TargetWalkerFn

type TargetWalkerFn func(ctx TargetContext, x Target) TargetDecision

TargetWalkerFn is used to implement a visitor pattern over types which implement Target.

Implementations of this function return a TargetDecision, which allows the function to control traversal. The zero value of TargetDecision means "continue". Other values can be obtained from the provided TargetContext to stop or to return an error.

A TargetDecision can also specify a post-visit function to execute or can be used to replace the value being visited.

type Targets

type Targets []Target

Targets is a named slice of a visitable interface.

type Unionable

type Unionable interface {
	// contains filtered or unexported methods
}

Unionable demonstrates how multiple type hierarchies can be unioned using another generated interface.

type UnionableType

type UnionableType struct{}

UnionableType will be included only when in --union mode.

Directories

Path Synopsis
Package other is used to check reachable types declared in other packages.
Package other is used to check reachable types declared in other packages.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL