ast

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Nov 15, 2017 License: Apache-2.0 Imports: 2 Imported by: 0

Documentation

Index

Constants

View Source
const (
	KInvalid = Kind(iota)

	KArg
	KAssert
	KAssign
	KConst
	KExpr
	KField
	KFile
	KFunc
	KIf
	KIterate
	KJump
	KPackageID
	KReturn
	KStatus
	KStruct
	KTypeExpr
	KUse
	KVar
	KWhile
)
View Source
const (
	FlagsImpure          = Flags(0x00000001)
	FlagsSuspendible     = Flags(0x00000002)
	FlagsCallImpure      = Flags(0x00000004)
	FlagsCallSuspendible = Flags(0x00000008)
	FlagsPublic          = Flags(0x00000010)
	FlagsTypeChecked     = Flags(0x00000020)
	FlagsHasBreak        = Flags(0x00000040)
	FlagsHasContinue     = Flags(0x00000080)
	FlagsGlobalIdent     = Flags(0x00000100)
)
View Source
const (
	// FlagsProvenNotToSuspend notes that a method such as read_u8 or
	// write_u16le is proven not to suspend. For example, if it's guaranteed
	// that there enough buffer space for an I/O method to succeed.
	//
	// Some methods, such as unread_u8, are called with the question mark, as
	// "r.unread_u8?()", so nominally can suspend, but their semantics also
	// guarantee to either succeed or fail, never suspend and retry later.
	FlagsProvenNotToSuspend = Flags(0x00010000)
	// FlagsBoundsCheckOptimized similarly means that some code gen's run time
	// bounds checks or null pointer checks can be skipped, although such
	// methods are not called with the question mark. Specifically, those
	// methods are:
	//  - since_mark
	FlagsBoundsCheckOptimized = Flags(0x00020000)
)

These flags are set by the bounds checker to generate optimized code.

View Source
const MaxBodyDepth = 255

MaxBodyDepth is an advisory limit for a function body's recursion depth.

View Source
const MaxExprDepth = 255

MaxExprDepth is an advisory limit for an Expr's recursion depth.

View Source
const MaxTypeExprDepth = 63

MaxTypeExprDepth is an advisory limit for a TypeExpr's recursion depth.

Variables

This section is empty.

Functions

This section is empty.

Types

type Arg

type Arg Node

Arg is "name:value".

  • ID1: <ident> name
  • RHS: <Expr> value

func NewArg

func NewArg(name t.ID, value *Expr) *Arg

func (*Arg) Name

func (n *Arg) Name() t.ID

func (*Arg) Node

func (n *Arg) Node() *Node

func (*Arg) Value

func (n *Arg) Value() *Expr

type Assert

type Assert Node

Assert is "assert RHS via ID1(args)", "pre etc", "inv etc" or "post etc":

  • ID0: <IDAssert|IDPre|IDInv|IDPost>
  • ID1: <string literal> reason
  • RHS: <Expr>
  • List0: <Arg> reason arguments

func NewAssert

func NewAssert(keyword t.ID, condition *Expr, reason t.ID, args []*Node) *Assert

func (*Assert) Args

func (n *Assert) Args() []*Node

func (*Assert) Condition

func (n *Assert) Condition() *Expr

func (*Assert) Keyword

func (n *Assert) Keyword() t.ID

func (*Assert) Node

func (n *Assert) Node() *Node

func (*Assert) Reason

func (n *Assert) Reason() t.ID

type Assign

type Assign Node

Assign is "LHS = RHS" or "LHS op= RHS":

  • ID0: operator
  • LHS: <Expr>
  • RHS: <Expr>

func NewAssign

func NewAssign(operator t.ID, lhs *Expr, rhs *Expr) *Assign

func (*Assign) LHS

func (n *Assign) LHS() *Expr

func (*Assign) Node

func (n *Assign) Node() *Node

func (*Assign) Operator

func (n *Assign) Operator() t.ID

func (*Assign) RHS

func (n *Assign) RHS() *Expr

type Const

type Const Node

Const is "const ID1 LHS = RHS":

  • FlagsPublic is "pub" vs "pri"
  • ID1: name
  • LHS: <TypeExpr>
  • RHS: <Expr>

func NewConst

func NewConst(flags Flags, filename string, line uint32, name t.ID, xType *TypeExpr, value *Expr) *Const

func (*Const) Filename

func (n *Const) Filename() string

func (*Const) Line

func (n *Const) Line() uint32

func (*Const) Name

func (n *Const) Name() t.ID

func (*Const) Node

func (n *Const) Node() *Node

func (*Const) Public

func (n *Const) Public() bool

func (*Const) Value

func (n *Const) Value() *Expr

func (*Const) XType

func (n *Const) XType() *TypeExpr

type Expr

type Expr Node

Expr is an expression, such as "i", "+j" or "k + l[m(n, o)].p":

  • FlagsImpure is if it or a sub-expr is FlagsCallImpure
  • FlagsSuspendible is if it or a sub-expr is FlagsCallSuspendible
  • FlagsCallImpure is "f(x)" vs "f!(x)"
  • FlagsCallSuspendible is "f(x)" vs "f?(x)", it implies FlagsCallImpure
  • ID0: <0|operator|IDOpenParen|IDOpenBracket|IDColon|IDDot>
  • ID1: <0|ident|literal>
  • LHS: <nil|Expr>
  • MHS: <nil|Expr>
  • RHS: <nil|Expr|TypeExpr>
  • List0: <Arg|Expr> function call args, assoc. op args or list members.

A zero ID0 means an identifier or literal in ID1, like "foo" or "42".

For unary operators, ID0 is the operator and RHS is the operand.

For binary operators, ID0 is the operator and LHS and RHS are the operands.

For associative operators, ID0 is the operator and List0 holds the operands.

The ID0 operator is in disambiguous form. For example, IDXUnaryPlus, IDXBinaryPlus or IDXAssociativePlus, not a bare IDPlus.

For function calls, like "LHS(List0)", ID0 is IDOpenParen.

For try/function calls, like "try LHS(List0)", ID0 is IDTry.

For indexes, like "LHS[RHS]", ID0 is IDOpenBracket.

For slices, like "LHS[MHS:RHS]", ID0 is IDColon.

For selectors, like "LHS.ID1", ID0 is IDDot.

For lists, like "$(0, 1, 2)", ID0 is IDDollar.

For statuses, like `error "foo"` and `"suspension "bar"`, ID0 is the keyword and ID1 is the message.

func NewExpr

func NewExpr(flags Flags, operator t.ID, nameLiteralSelector t.ID, lhs *Node, mhs *Node, rhs *Node, args []*Node) *Expr

func (*Expr) Args

func (n *Expr) Args() []*Node

func (*Expr) BoundsCheckOptimized

func (n *Expr) BoundsCheckOptimized() bool

func (*Expr) CallImpure

func (n *Expr) CallImpure() bool

func (*Expr) CallSuspendible

func (n *Expr) CallSuspendible() bool

func (*Expr) ConstValue

func (n *Expr) ConstValue() *big.Int

func (*Expr) Eq

func (n *Expr) Eq(o *Expr) bool

Eq returns whether n and o are equal.

It may return false negatives. In general, it will not report that "x + y" equals "y + x". However, if both are constant expressions (i.e. each Expr node, including the sum nodes, has a ConstValue), both sums will have the same value and will compare equal.

func (*Expr) GlobalIdent

func (n *Expr) GlobalIdent() bool

func (*Expr) ID0

func (n *Expr) ID0() t.ID

func (*Expr) ID1

func (n *Expr) ID1() t.ID

func (*Expr) Impure

func (n *Expr) Impure() bool

func (*Expr) LHS

func (n *Expr) LHS() *Node

func (*Expr) MHS

func (n *Expr) MHS() *Node

func (*Expr) MType

func (n *Expr) MType() *TypeExpr

func (*Expr) Mentions

func (n *Expr) Mentions(o *Expr) bool

func (*Expr) Node

func (n *Expr) Node() *Node

func (*Expr) ProvenNotToSuspend

func (n *Expr) ProvenNotToSuspend() bool

func (*Expr) Pure

func (n *Expr) Pure() bool

func (*Expr) RHS

func (n *Expr) RHS() *Node

func (*Expr) SetBoundsCheckOptimized

func (n *Expr) SetBoundsCheckOptimized()

func (*Expr) SetConstValue

func (n *Expr) SetConstValue(x *big.Int)

func (*Expr) SetGlobalIdent

func (n *Expr) SetGlobalIdent()

func (*Expr) SetMType

func (n *Expr) SetMType(x *TypeExpr)

func (*Expr) SetProvenNotToSuspend

func (n *Expr) SetProvenNotToSuspend()

func (*Expr) String

func (n *Expr) String(tm *t.Map) string

String returns a string form of n.

func (*Expr) Suspendible

func (n *Expr) Suspendible() bool

type Field

type Field Node

Field is a "name type = default_value" struct field:

  • ID1: name
  • LHS: <TypeExpr>
  • RHS: <nil|Expr>

func NewField

func NewField(name t.ID, xType *TypeExpr, defaultValue *Expr) *Field

func (*Field) DefaultValue

func (n *Field) DefaultValue() *Expr

func (*Field) Name

func (n *Field) Name() t.ID

func (*Field) Node

func (n *Field) Node() *Node

func (*Field) XType

func (n *Field) XType() *TypeExpr

type File

type File Node

File is a file of source code:

  • List0: <Func|PackageID|Status|Struct|Use> top-level declarations

func NewFile

func NewFile(filename string, topLevelDecls []*Node) *File

func (*File) Filename

func (n *File) Filename() string

func (*File) Node

func (n *File) Node() *Node

func (*File) TopLevelDecls

func (n *File) TopLevelDecls() []*Node

type Flags

type Flags uint32

type Func

type Func Node

Func is "func ID0.ID1(LHS)(RHS) { List2 }":

  • FlagsImpure is "ID1" vs "ID1!"
  • FlagsSuspendible is "ID1" vs "ID1?", it implies FlagsImpure
  • FlagsPublic is "pub" vs "pri"
  • ID0: <0|receiver>
  • ID1: name
  • LHS: <Struct> in-parameters
  • RHS: <Struct> out-parameters
  • List1: <Assert> asserts
  • List2: <Statement> function body

Statement means one of:

  • Assert
  • Assign
  • Expr
  • If
  • Iterate
  • Jump
  • Return
  • Var
  • While

func NewFunc

func NewFunc(flags Flags, filename string, line uint32, receiver t.ID, name t.ID, in *Struct, out *Struct, asserts []*Node, body []*Node) *Func

func (*Func) Asserts

func (n *Func) Asserts() []*Node

func (*Func) Body

func (n *Func) Body() []*Node

func (*Func) Filename

func (n *Func) Filename() string

func (*Func) Impure

func (n *Func) Impure() bool

func (*Func) In

func (n *Func) In() *Struct

func (*Func) Line

func (n *Func) Line() uint32

func (*Func) Name

func (n *Func) Name() t.ID

func (*Func) Node

func (n *Func) Node() *Node

func (*Func) Out

func (n *Func) Out() *Struct

func (*Func) Public

func (n *Func) Public() bool

func (*Func) Pure

func (n *Func) Pure() bool

func (*Func) QID

func (n *Func) QID() t.QID

func (*Func) Receiver

func (n *Func) Receiver() t.ID

func (*Func) Suspendible

func (n *Func) Suspendible() bool

type If

type If Node

If is "if MHS { List0 } else RHS" or "if MHS { List0 } else { List1 }":

  • MHS: <Expr>
  • RHS: <nil|If>
  • List0: <Statement> if-true body
  • List1: <Statement> if-false body

func NewIf

func NewIf(condition *Expr, elseIf *If, bodyIfTrue []*Node, bodyIfFalse []*Node) *If

func (*If) BodyIfFalse

func (n *If) BodyIfFalse() []*Node

func (*If) BodyIfTrue

func (n *If) BodyIfTrue() []*Node

func (*If) Condition

func (n *If) Condition() *Expr

func (*If) ElseIf

func (n *If) ElseIf() *If

func (*If) Node

func (n *If) Node() *Node

type Iterate

type Iterate Node

Iterate is "iterate.LHS:ID1 (vars), List1 { List2 }":

  • FlagsHasBreak is the iterate has an explicit break
  • FlagsHasContinue is the iterate has an explicit continue
  • ID1: <0|label>
  • List0: <Var> variables
  • List1: <Assert> asserts
  • List2: <Statement> loop body

func NewIterate

func NewIterate(label t.ID, unrollCount *Expr, variables []*Node, asserts []*Node, body []*Node) *Iterate

func (*Iterate) Asserts

func (n *Iterate) Asserts() []*Node

func (*Iterate) Body

func (n *Iterate) Body() []*Node

func (*Iterate) HasBreak

func (n *Iterate) HasBreak() bool

func (*Iterate) HasContinue

func (n *Iterate) HasContinue() bool

func (*Iterate) Label

func (n *Iterate) Label() t.ID

func (*Iterate) Node

func (n *Iterate) Node() *Node

func (*Iterate) SetHasBreak

func (n *Iterate) SetHasBreak()

func (*Iterate) SetHasContinue

func (n *Iterate) SetHasContinue()

func (*Iterate) UnrollCount

func (n *Iterate) UnrollCount() *Expr

func (*Iterate) Variables

func (n *Iterate) Variables() []*Node

type Jump

type Jump Node

Jump is "break" or "continue", with an optional label, "break:label":

  • ID0: <IDBreak|IDContinue>
  • ID1: <0|label>

func NewJump

func NewJump(keyword t.ID, label t.ID) *Jump

func (*Jump) JumpTarget

func (n *Jump) JumpTarget() Loop

func (*Jump) Keyword

func (n *Jump) Keyword() t.ID

func (*Jump) Label

func (n *Jump) Label() t.ID

func (*Jump) Node

func (n *Jump) Node() *Node

func (*Jump) SetJumpTarget

func (n *Jump) SetJumpTarget(o Loop)

type Kind

type Kind uint32

Kind is what kind of node it is. For example, a top-level func or a numeric constant. Kind is different from Type; the latter is used for type-checking in the programming language sense.

func (Kind) String

func (k Kind) String() string

type Loop

type Loop interface {
	Node() *Node
	HasBreak() bool
	HasContinue() bool
	Label() t.ID
	Asserts() []*Node
	Body() []*Node
	SetHasBreak()
	SetHasContinue()
}

type Node

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

func (*Node) Arg

func (n *Node) Arg() *Arg

func (*Node) Assert

func (n *Node) Assert() *Assert

func (*Node) Assign

func (n *Node) Assign() *Assign

func (*Node) Const

func (n *Node) Const() *Const

func (*Node) Expr

func (n *Node) Expr() *Expr

func (*Node) Field

func (n *Node) Field() *Field

func (*Node) File

func (n *Node) File() *File

func (*Node) Func

func (n *Node) Func() *Func

func (*Node) If

func (n *Node) If() *If

func (*Node) Iterate

func (n *Node) Iterate() *Iterate

func (*Node) Jump

func (n *Node) Jump() *Jump

func (*Node) Kind

func (n *Node) Kind() Kind

func (*Node) PackageID

func (n *Node) PackageID() *PackageID

func (*Node) Raw

func (n *Node) Raw() *Raw

func (*Node) Return

func (n *Node) Return() *Return

func (*Node) SetTypeChecked

func (n *Node) SetTypeChecked()

func (*Node) Status

func (n *Node) Status() *Status

func (*Node) Struct

func (n *Node) Struct() *Struct

func (*Node) TypeChecked

func (n *Node) TypeChecked() bool

func (*Node) TypeExpr

func (n *Node) TypeExpr() *TypeExpr

func (*Node) Use

func (n *Node) Use() *Use

func (*Node) Var

func (n *Node) Var() *Var

func (*Node) Walk

func (n *Node) Walk(f func(*Node) error) error

func (*Node) While

func (n *Node) While() *While

type PackageID

type PackageID Node

PackageID is "packageid ID1":

  • ID1: <string literal> package ID

func NewPackageID

func NewPackageID(filename string, line uint32, id t.ID) *PackageID

func (*PackageID) Filename

func (n *PackageID) Filename() string

func (*PackageID) ID

func (n *PackageID) ID() t.ID

func (*PackageID) Line

func (n *PackageID) Line() uint32

func (*PackageID) Node

func (n *PackageID) Node() *Node

type Raw

type Raw Node

func (*Raw) ConstValue

func (n *Raw) ConstValue() *big.Int

func (*Raw) Filename

func (n *Raw) Filename() string

func (*Raw) FilenameLine

func (n *Raw) FilenameLine() (string, uint32)

func (*Raw) Flags

func (n *Raw) Flags() Flags

func (*Raw) ID0

func (n *Raw) ID0() t.ID

func (*Raw) ID1

func (n *Raw) ID1() t.ID

func (*Raw) Kind

func (n *Raw) Kind() Kind

func (*Raw) LHS

func (n *Raw) LHS() *Node

func (*Raw) Line

func (n *Raw) Line() uint32

func (*Raw) List0

func (n *Raw) List0() []*Node

func (*Raw) List1

func (n *Raw) List1() []*Node

func (*Raw) List2

func (n *Raw) List2() []*Node

func (*Raw) MHS

func (n *Raw) MHS() *Node

func (*Raw) MType

func (n *Raw) MType() *TypeExpr

func (*Raw) Node

func (n *Raw) Node() *Node

func (*Raw) QID

func (n *Raw) QID() t.QID

func (*Raw) RHS

func (n *Raw) RHS() *Node

func (*Raw) SetFilenameLine

func (n *Raw) SetFilenameLine(f string, l uint32)

func (*Raw) SubLists

func (n *Raw) SubLists() [3][]*Node

func (*Raw) SubNodes

func (n *Raw) SubNodes() [3]*Node

type Return

type Return Node

Return is "return LHS":

  • LHS: <nil|Expr>

func NewReturn

func NewReturn(value *Expr) *Return

func (*Return) Node

func (n *Return) Node() *Node

func (*Return) Value

func (n *Return) Value() *Expr

type Status

type Status Node

Status is "error ID1" or "suspension ID1":

  • FlagsPublic is "pub" vs "pri"
  • ID0: <IDError|IDSuspension>
  • ID1: message

func NewStatus

func NewStatus(flags Flags, filename string, line uint32, keyword t.ID, message t.ID) *Status

func (*Status) Filename

func (n *Status) Filename() string

func (*Status) Keyword

func (n *Status) Keyword() t.ID

func (*Status) Line

func (n *Status) Line() uint32

func (*Status) Message

func (n *Status) Message() t.ID

func (*Status) Node

func (n *Status) Node() *Node

func (*Status) Public

func (n *Status) Public() bool

type Struct

type Struct Node

Struct is "struct ID1(List0)":

  • FlagsSuspendible is "ID1" vs "ID1?"
  • FlagsPublic is "pub" vs "pri"
  • ID1: name
  • List0: <Field> fields

func NewStruct

func NewStruct(flags Flags, filename string, line uint32, name t.ID, fields []*Node) *Struct

func TopologicalSortStructs

func TopologicalSortStructs(ns []*Struct) (sorted []*Struct, ok bool)

func (*Struct) Fields

func (n *Struct) Fields() []*Node

func (*Struct) Filename

func (n *Struct) Filename() string

func (*Struct) Line

func (n *Struct) Line() uint32

func (*Struct) Name

func (n *Struct) Name() t.ID

func (*Struct) Node

func (n *Struct) Node() *Node

func (*Struct) Public

func (n *Struct) Public() bool

func (*Struct) Suspendible

func (n *Struct) Suspendible() bool

type TypeExpr

type TypeExpr Node

TypeExpr is a type expression, such as "u32", "u32[..8]", "pkg.foo", "ptr T", "[8] T" or "[] T":

  • ID0: <0|package name|IDPtr|IDOpenBracket|IDColon>
  • ID1: <0|type name>
  • LHS: <nil|Expr>
  • MHS: <nil|Expr>
  • RHS: <nil|TypeExpr>

An IDPtr ID0 means "ptr RHS". RHS is the inner type.

An IDOpenBracket ID0 means "[LHS] RHS". RHS is the inner type.

An IDColon ID0 means "[] RHS". RHS is the inner type.

Other ID0 values mean a (possibly package-qualified) type like "pkg.foo" or "foo". ID0 is the "pkg" or zero, ID1 is the "foo". Such a type can be refined as "foo[LHS..MHS]". LHS and MHS are Expr's, possibly nil. For example, the LHS for "u32[..4095]" is nil.

TODO: function / method types, struct types, list types.

func NewTypeExpr

func NewTypeExpr(pkgOrDec t.ID, name t.ID, arrayLengthMin *Expr, max *Expr, inner *TypeExpr) *TypeExpr

func (*TypeExpr) ArrayLength

func (n *TypeExpr) ArrayLength() *Expr

func (*TypeExpr) Bounds

func (n *TypeExpr) Bounds() [2]*Expr

func (*TypeExpr) Decorator

func (n *TypeExpr) Decorator() t.ID

func (*TypeExpr) Eq

func (n *TypeExpr) Eq(o *TypeExpr) bool

Eq returns whether n and o are equal.

func (*TypeExpr) EqIgnoringRefinements

func (n *TypeExpr) EqIgnoringRefinements(o *TypeExpr) bool

EqIgnoringRefinements returns whether n and o are equal, ignoring the "[i:j]" in "u32[i:j]".

func (*TypeExpr) HasPointers

func (n *TypeExpr) HasPointers() bool

func (*TypeExpr) Inner

func (n *TypeExpr) Inner() *TypeExpr

func (*TypeExpr) Innermost

func (n *TypeExpr) Innermost() *TypeExpr

func (*TypeExpr) IsBool

func (n *TypeExpr) IsBool() bool

func (*TypeExpr) IsIdeal

func (n *TypeExpr) IsIdeal() bool

func (*TypeExpr) IsNumType

func (n *TypeExpr) IsNumType() bool

func (*TypeExpr) IsNumTypeOrIdeal

func (n *TypeExpr) IsNumTypeOrIdeal() bool

func (*TypeExpr) IsRefined

func (n *TypeExpr) IsRefined() bool

func (*TypeExpr) IsUnsignedInteger

func (n *TypeExpr) IsUnsignedInteger() bool

func (*TypeExpr) Max

func (n *TypeExpr) Max() *Expr

func (*TypeExpr) Min

func (n *TypeExpr) Min() *Expr

func (*TypeExpr) Name

func (n *TypeExpr) Name() t.ID

func (*TypeExpr) Node

func (n *TypeExpr) Node() *Node

func (*TypeExpr) String

func (n *TypeExpr) String(tm *t.Map) string

String returns a string form of n.

func (*TypeExpr) Unrefined

func (n *TypeExpr) Unrefined() *TypeExpr

type Use

type Use Node

Use is "use ID1":

  • ID1: <string literal> package path

func NewUse

func NewUse(filename string, line uint32, path t.ID) *Use

func (*Use) Filename

func (n *Use) Filename() string

func (*Use) Line

func (n *Use) Line() uint32

func (*Use) Node

func (n *Use) Node() *Node

func (*Use) Path

func (n *Use) Path() t.ID

type Var

type Var Node

Var is "var ID1 LHS" or "var ID1 LHS = RHS" or an iterate variable declaration "ID1 LHS : RHS":

  • ID0: <0|IDEq|IDColon>
  • ID1: name
  • LHS: <TypeExpr>
  • RHS: <nil|Expr>

func NewVar

func NewVar(op t.ID, name t.ID, xType *TypeExpr, value *Expr) *Var

func (*Var) IterateVariable

func (n *Var) IterateVariable() bool

func (*Var) Name

func (n *Var) Name() t.ID

func (*Var) Node

func (n *Var) Node() *Node

func (*Var) Value

func (n *Var) Value() *Expr

func (*Var) XType

func (n *Var) XType() *TypeExpr

type While

type While Node

While is "while:ID1 MHS, List1 { List2 }":

  • FlagsHasBreak is the while has an explicit break
  • FlagsHasContinue is the while has an explicit continue
  • ID1: <0|label>
  • MHS: <Expr>
  • List1: <Assert> asserts
  • List2: <Statement> loop body

TODO: should we be able to unroll while loops too?

func NewWhile

func NewWhile(label t.ID, condition *Expr, asserts []*Node, body []*Node) *While

func (*While) Asserts

func (n *While) Asserts() []*Node

func (*While) Body

func (n *While) Body() []*Node

func (*While) Condition

func (n *While) Condition() *Expr

func (*While) HasBreak

func (n *While) HasBreak() bool

func (*While) HasContinue

func (n *While) HasContinue() bool

func (*While) Label

func (n *While) Label() t.ID

func (*While) Node

func (n *While) Node() *Node

func (*While) SetHasBreak

func (n *While) SetHasBreak()

func (*While) SetHasContinue

func (n *While) SetHasContinue()

Jump to

Keyboard shortcuts

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