123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785 |
- // Package ast provides structures to represent a handlebars Abstract Syntax Tree, and a Visitor interface to visit that tree.
- package ast
- import (
- "fmt"
- "strconv"
- )
- // References:
- // - https://github.com/wycats/handlebars.js/blob/master/lib/handlebars/compiler/ast.js
- // - https://github.com/wycats/handlebars.js/blob/master/docs/compiler-api.md
- // - https://github.com/golang/go/blob/master/src/text/template/parse/node.go
- // Node is an element in the AST.
- type Node interface {
- // node type
- Type() NodeType
- // location of node in original input string
- Location() Loc
- // string representation, used for debugging
- String() string
- // accepts visitor
- Accept(Visitor) interface{}
- }
- // Visitor is the interface to visit an AST.
- type Visitor interface {
- VisitProgram(*Program) interface{}
- // statements
- VisitMustache(*MustacheStatement) interface{}
- VisitBlock(*BlockStatement) interface{}
- VisitPartial(*PartialStatement) interface{}
- VisitContent(*ContentStatement) interface{}
- VisitComment(*CommentStatement) interface{}
- // expressions
- VisitExpression(*Expression) interface{}
- VisitSubExpression(*SubExpression) interface{}
- VisitPath(*PathExpression) interface{}
- // literals
- VisitString(*StringLiteral) interface{}
- VisitBoolean(*BooleanLiteral) interface{}
- VisitNumber(*NumberLiteral) interface{}
- // miscellaneous
- VisitHash(*Hash) interface{}
- VisitHashPair(*HashPair) interface{}
- }
- // NodeType represents an AST Node type.
- type NodeType int
- // Type returns itself, and permits struct includers to satisfy that part of Node interface.
- func (t NodeType) Type() NodeType {
- return t
- }
- const (
- // NodeProgram is the program node
- NodeProgram NodeType = iota
- // NodeMustache is the mustache statement node
- NodeMustache
- // NodeBlock is the block statement node
- NodeBlock
- // NodePartial is the partial statement node
- NodePartial
- // NodeContent is the content statement node
- NodeContent
- // NodeComment is the comment statement node
- NodeComment
- // NodeExpression is the expression node
- NodeExpression
- // NodeSubExpression is the subexpression node
- NodeSubExpression
- // NodePath is the expression path node
- NodePath
- // NodeBoolean is the literal boolean node
- NodeBoolean
- // NodeNumber is the literal number node
- NodeNumber
- // NodeString is the literal string node
- NodeString
- // NodeHash is the hash node
- NodeHash
- // NodeHashPair is the hash pair node
- NodeHashPair
- )
- // Loc represents the position of a parsed node in source file.
- type Loc struct {
- Pos int // Byte position
- Line int // Line number
- }
- // Location returns itself, and permits struct includers to satisfy that part of Node interface.
- func (l Loc) Location() Loc {
- return l
- }
- // Strip describes node whitespace management.
- type Strip struct {
- Open bool
- Close bool
- OpenStandalone bool
- CloseStandalone bool
- InlineStandalone bool
- }
- // NewStrip instanciates a Strip for given open and close mustaches.
- func NewStrip(openStr, closeStr string) *Strip {
- return &Strip{
- Open: (len(openStr) > 2) && openStr[2] == '~',
- Close: (len(closeStr) > 2) && closeStr[len(closeStr)-3] == '~',
- }
- }
- // NewStripForStr instanciates a Strip for given tag.
- func NewStripForStr(str string) *Strip {
- return &Strip{
- Open: (len(str) > 2) && str[2] == '~',
- Close: (len(str) > 2) && str[len(str)-3] == '~',
- }
- }
- // String returns a string representation of receiver that can be used for debugging.
- func (s *Strip) String() string {
- return fmt.Sprintf("Open: %t, Close: %t, OpenStandalone: %t, CloseStandalone: %t, InlineStandalone: %t", s.Open, s.Close, s.OpenStandalone, s.CloseStandalone, s.InlineStandalone)
- }
- //
- // Program
- //
- // Program represents a program node.
- type Program struct {
- NodeType
- Loc
- Body []Node // [ Statement ... ]
- BlockParams []string
- Chained bool
- // whitespace management
- Strip *Strip
- }
- // NewProgram instanciates a new program node.
- func NewProgram(pos int, line int) *Program {
- return &Program{
- NodeType: NodeProgram,
- Loc: Loc{pos, line},
- }
- }
- // String returns a string representation of receiver that can be used for debugging.
- func (node *Program) String() string {
- return fmt.Sprintf("Program{Pos: %d}", node.Loc.Pos)
- }
- // Accept is the receiver entry point for visitors.
- func (node *Program) Accept(visitor Visitor) interface{} {
- return visitor.VisitProgram(node)
- }
- // AddStatement adds given statement to program.
- func (node *Program) AddStatement(statement Node) {
- node.Body = append(node.Body, statement)
- }
- //
- // Mustache Statement
- //
- // MustacheStatement represents a mustache node.
- type MustacheStatement struct {
- NodeType
- Loc
- Unescaped bool
- Expression *Expression
- // whitespace management
- Strip *Strip
- }
- // NewMustacheStatement instanciates a new mustache node.
- func NewMustacheStatement(pos int, line int, unescaped bool) *MustacheStatement {
- return &MustacheStatement{
- NodeType: NodeMustache,
- Loc: Loc{pos, line},
- Unescaped: unescaped,
- }
- }
- // String returns a string representation of receiver that can be used for debugging.
- func (node *MustacheStatement) String() string {
- return fmt.Sprintf("Mustache{Pos: %d}", node.Loc.Pos)
- }
- // Accept is the receiver entry point for visitors.
- func (node *MustacheStatement) Accept(visitor Visitor) interface{} {
- return visitor.VisitMustache(node)
- }
- //
- // Block Statement
- //
- // BlockStatement represents a block node.
- type BlockStatement struct {
- NodeType
- Loc
- Expression *Expression
- Program *Program
- Inverse *Program
- // whitespace management
- OpenStrip *Strip
- InverseStrip *Strip
- CloseStrip *Strip
- }
- // NewBlockStatement instanciates a new block node.
- func NewBlockStatement(pos int, line int) *BlockStatement {
- return &BlockStatement{
- NodeType: NodeBlock,
- Loc: Loc{pos, line},
- }
- }
- // String returns a string representation of receiver that can be used for debugging.
- func (node *BlockStatement) String() string {
- return fmt.Sprintf("Block{Pos: %d}", node.Loc.Pos)
- }
- // Accept is the receiver entry point for visitors.
- func (node *BlockStatement) Accept(visitor Visitor) interface{} {
- return visitor.VisitBlock(node)
- }
- //
- // Partial Statement
- //
- // PartialStatement represents a partial node.
- type PartialStatement struct {
- NodeType
- Loc
- Name Node // PathExpression | SubExpression
- Params []Node // [ Expression ... ]
- Hash *Hash
- // whitespace management
- Strip *Strip
- Indent string
- }
- // NewPartialStatement instanciates a new partial node.
- func NewPartialStatement(pos int, line int) *PartialStatement {
- return &PartialStatement{
- NodeType: NodePartial,
- Loc: Loc{pos, line},
- }
- }
- // String returns a string representation of receiver that can be used for debugging.
- func (node *PartialStatement) String() string {
- return fmt.Sprintf("Partial{Name:%s, Pos:%d}", node.Name, node.Loc.Pos)
- }
- // Accept is the receiver entry point for visitors.
- func (node *PartialStatement) Accept(visitor Visitor) interface{} {
- return visitor.VisitPartial(node)
- }
- //
- // Content Statement
- //
- // ContentStatement represents a content node.
- type ContentStatement struct {
- NodeType
- Loc
- Value string
- Original string
- // whitespace management
- RightStripped bool
- LeftStripped bool
- }
- // NewContentStatement instanciates a new content node.
- func NewContentStatement(pos int, line int, val string) *ContentStatement {
- return &ContentStatement{
- NodeType: NodeContent,
- Loc: Loc{pos, line},
- Value: val,
- Original: val,
- }
- }
- // String returns a string representation of receiver that can be used for debugging.
- func (node *ContentStatement) String() string {
- return fmt.Sprintf("Content{Value:'%s', Pos:%d}", node.Value, node.Loc.Pos)
- }
- // Accept is the receiver entry point for visitors.
- func (node *ContentStatement) Accept(visitor Visitor) interface{} {
- return visitor.VisitContent(node)
- }
- //
- // Comment Statement
- //
- // CommentStatement represents a comment node.
- type CommentStatement struct {
- NodeType
- Loc
- Value string
- // whitespace management
- Strip *Strip
- }
- // NewCommentStatement instanciates a new comment node.
- func NewCommentStatement(pos int, line int, val string) *CommentStatement {
- return &CommentStatement{
- NodeType: NodeComment,
- Loc: Loc{pos, line},
- Value: val,
- }
- }
- // String returns a string representation of receiver that can be used for debugging.
- func (node *CommentStatement) String() string {
- return fmt.Sprintf("Comment{Value:'%s', Pos:%d}", node.Value, node.Loc.Pos)
- }
- // Accept is the receiver entry point for visitors.
- func (node *CommentStatement) Accept(visitor Visitor) interface{} {
- return visitor.VisitComment(node)
- }
- //
- // Expression
- //
- // Expression represents an expression node.
- type Expression struct {
- NodeType
- Loc
- Path Node // PathExpression | StringLiteral | BooleanLiteral | NumberLiteral
- Params []Node // [ Expression ... ]
- Hash *Hash
- }
- // NewExpression instanciates a new expression node.
- func NewExpression(pos int, line int) *Expression {
- return &Expression{
- NodeType: NodeExpression,
- Loc: Loc{pos, line},
- }
- }
- // String returns a string representation of receiver that can be used for debugging.
- func (node *Expression) String() string {
- return fmt.Sprintf("Expr{Path:%s, Pos:%d}", node.Path, node.Loc.Pos)
- }
- // Accept is the receiver entry point for visitors.
- func (node *Expression) Accept(visitor Visitor) interface{} {
- return visitor.VisitExpression(node)
- }
- // HelperName returns helper name, or an empty string if this expression can't be a helper.
- func (node *Expression) HelperName() string {
- path, ok := node.Path.(*PathExpression)
- if !ok {
- return ""
- }
- if path.Data || (len(path.Parts) != 1) || (path.Depth > 0) || path.Scoped {
- return ""
- }
- return path.Parts[0]
- }
- // FieldPath returns path expression representing a field path, or nil if this is not a field path.
- func (node *Expression) FieldPath() *PathExpression {
- path, ok := node.Path.(*PathExpression)
- if !ok {
- return nil
- }
- return path
- }
- // LiteralStr returns the string representation of literal value, with a boolean set to false if this is not a literal.
- func (node *Expression) LiteralStr() (string, bool) {
- return LiteralStr(node.Path)
- }
- // Canonical returns the canonical form of expression node as a string.
- func (node *Expression) Canonical() string {
- if str, ok := HelperNameStr(node.Path); ok {
- return str
- }
- return ""
- }
- // HelperNameStr returns the string representation of a helper name, with a boolean set to false if this is not a valid helper name.
- //
- // helperName : path | dataName | STRING | NUMBER | BOOLEAN | UNDEFINED | NULL
- func HelperNameStr(node Node) (string, bool) {
- // PathExpression
- if str, ok := PathExpressionStr(node); ok {
- return str, ok
- }
- // Literal
- if str, ok := LiteralStr(node); ok {
- return str, ok
- }
- return "", false
- }
- // PathExpressionStr returns the string representation of path expression value, with a boolean set to false if this is not a path expression.
- func PathExpressionStr(node Node) (string, bool) {
- if path, ok := node.(*PathExpression); ok {
- result := path.Original
- // "[foo bar]"" => "foo bar"
- if (len(result) >= 2) && (result[0] == '[') && (result[len(result)-1] == ']') {
- result = result[1 : len(result)-1]
- }
- return result, true
- }
- return "", false
- }
- // LiteralStr returns the string representation of literal value, with a boolean set to false if this is not a literal.
- func LiteralStr(node Node) (string, bool) {
- if lit, ok := node.(*StringLiteral); ok {
- return lit.Value, true
- }
- if lit, ok := node.(*BooleanLiteral); ok {
- return lit.Canonical(), true
- }
- if lit, ok := node.(*NumberLiteral); ok {
- return lit.Canonical(), true
- }
- return "", false
- }
- //
- // SubExpression
- //
- // SubExpression represents a subexpression node.
- type SubExpression struct {
- NodeType
- Loc
- Expression *Expression
- }
- // NewSubExpression instanciates a new subexpression node.
- func NewSubExpression(pos int, line int) *SubExpression {
- return &SubExpression{
- NodeType: NodeSubExpression,
- Loc: Loc{pos, line},
- }
- }
- // String returns a string representation of receiver that can be used for debugging.
- func (node *SubExpression) String() string {
- return fmt.Sprintf("Sexp{Path:%s, Pos:%d}", node.Expression.Path, node.Loc.Pos)
- }
- // Accept is the receiver entry point for visitors.
- func (node *SubExpression) Accept(visitor Visitor) interface{} {
- return visitor.VisitSubExpression(node)
- }
- //
- // Path Expression
- //
- // PathExpression represents a path expression node.
- type PathExpression struct {
- NodeType
- Loc
- Original string
- Depth int
- Parts []string
- Data bool
- Scoped bool
- }
- // NewPathExpression instanciates a new path expression node.
- func NewPathExpression(pos int, line int, data bool) *PathExpression {
- result := &PathExpression{
- NodeType: NodePath,
- Loc: Loc{pos, line},
- Data: data,
- }
- if data {
- result.Original = "@"
- }
- return result
- }
- // String returns a string representation of receiver that can be used for debugging.
- func (node *PathExpression) String() string {
- return fmt.Sprintf("Path{Original:'%s', Pos:%d}", node.Original, node.Loc.Pos)
- }
- // Accept is the receiver entry point for visitors.
- func (node *PathExpression) Accept(visitor Visitor) interface{} {
- return visitor.VisitPath(node)
- }
- // Part adds path part.
- func (node *PathExpression) Part(part string) {
- node.Original += part
- switch part {
- case "..":
- node.Depth++
- node.Scoped = true
- case ".", "this":
- node.Scoped = true
- default:
- node.Parts = append(node.Parts, part)
- }
- }
- // Sep adds path separator.
- func (node *PathExpression) Sep(separator string) {
- node.Original += separator
- }
- // IsDataRoot returns true if path expression is @root.
- func (node *PathExpression) IsDataRoot() bool {
- return node.Data && (node.Parts[0] == "root")
- }
- //
- // String Literal
- //
- // StringLiteral represents a string node.
- type StringLiteral struct {
- NodeType
- Loc
- Value string
- }
- // NewStringLiteral instanciates a new string node.
- func NewStringLiteral(pos int, line int, val string) *StringLiteral {
- return &StringLiteral{
- NodeType: NodeString,
- Loc: Loc{pos, line},
- Value: val,
- }
- }
- // String returns a string representation of receiver that can be used for debugging.
- func (node *StringLiteral) String() string {
- return fmt.Sprintf("String{Value:'%s', Pos:%d}", node.Value, node.Loc.Pos)
- }
- // Accept is the receiver entry point for visitors.
- func (node *StringLiteral) Accept(visitor Visitor) interface{} {
- return visitor.VisitString(node)
- }
- //
- // Boolean Literal
- //
- // BooleanLiteral represents a boolean node.
- type BooleanLiteral struct {
- NodeType
- Loc
- Value bool
- Original string
- }
- // NewBooleanLiteral instanciates a new boolean node.
- func NewBooleanLiteral(pos int, line int, val bool, original string) *BooleanLiteral {
- return &BooleanLiteral{
- NodeType: NodeBoolean,
- Loc: Loc{pos, line},
- Value: val,
- Original: original,
- }
- }
- // String returns a string representation of receiver that can be used for debugging.
- func (node *BooleanLiteral) String() string {
- return fmt.Sprintf("Boolean{Value:%s, Pos:%d}", node.Canonical(), node.Loc.Pos)
- }
- // Accept is the receiver entry point for visitors.
- func (node *BooleanLiteral) Accept(visitor Visitor) interface{} {
- return visitor.VisitBoolean(node)
- }
- // Canonical returns the canonical form of boolean node as a string (ie. "true" | "false").
- func (node *BooleanLiteral) Canonical() string {
- if node.Value {
- return "true"
- }
- return "false"
- }
- //
- // Number Literal
- //
- // NumberLiteral represents a number node.
- type NumberLiteral struct {
- NodeType
- Loc
- Value float64
- IsInt bool
- Original string
- }
- // NewNumberLiteral instanciates a new number node.
- func NewNumberLiteral(pos int, line int, val float64, isInt bool, original string) *NumberLiteral {
- return &NumberLiteral{
- NodeType: NodeNumber,
- Loc: Loc{pos, line},
- Value: val,
- IsInt: isInt,
- Original: original,
- }
- }
- // String returns a string representation of receiver that can be used for debugging.
- func (node *NumberLiteral) String() string {
- return fmt.Sprintf("Number{Value:%s, Pos:%d}", node.Canonical(), node.Loc.Pos)
- }
- // Accept is the receiver entry point for visitors.
- func (node *NumberLiteral) Accept(visitor Visitor) interface{} {
- return visitor.VisitNumber(node)
- }
- // Canonical returns the canonical form of number node as a string (eg: "12", "-1.51").
- func (node *NumberLiteral) Canonical() string {
- prec := -1
- if node.IsInt {
- prec = 0
- }
- return strconv.FormatFloat(node.Value, 'f', prec, 64)
- }
- // Number returns an integer or a float.
- func (node *NumberLiteral) Number() interface{} {
- if node.IsInt {
- return int(node.Value)
- }
- return node.Value
- }
- //
- // Hash
- //
- // Hash represents a hash node.
- type Hash struct {
- NodeType
- Loc
- Pairs []*HashPair
- }
- // NewHash instanciates a new hash node.
- func NewHash(pos int, line int) *Hash {
- return &Hash{
- NodeType: NodeHash,
- Loc: Loc{pos, line},
- }
- }
- // String returns a string representation of receiver that can be used for debugging.
- func (node *Hash) String() string {
- result := fmt.Sprintf("Hash{[%d", node.Loc.Pos)
- for i, p := range node.Pairs {
- if i > 0 {
- result += ", "
- }
- result += p.String()
- }
- return result + fmt.Sprintf("], Pos:%d}", node.Loc.Pos)
- }
- // Accept is the receiver entry point for visitors.
- func (node *Hash) Accept(visitor Visitor) interface{} {
- return visitor.VisitHash(node)
- }
- //
- // HashPair
- //
- // HashPair represents a hash pair node.
- type HashPair struct {
- NodeType
- Loc
- Key string
- Val Node // Expression
- }
- // NewHashPair instanciates a new hash pair node.
- func NewHashPair(pos int, line int) *HashPair {
- return &HashPair{
- NodeType: NodeHashPair,
- Loc: Loc{pos, line},
- }
- }
- // String returns a string representation of receiver that can be used for debugging.
- func (node *HashPair) String() string {
- return node.Key + "=" + node.Val.String()
- }
- // Accept is the receiver entry point for visitors.
- func (node *HashPair) Accept(visitor Visitor) interface{} {
- return visitor.VisitHashPair(node)
- }
|