"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "tpl/internal/go_templates/texttemplate/parse/parse.go" between
hugo-0.80.0.tar.gz and hugo-0.81.0.tar.gz

About: Hugo is a static site generator that takes a source directory of Markdown files and templates and uses these as input to create a complete website (written in Go).

parse.go  (hugo-0.80.0):parse.go  (hugo-0.81.0)
skipping to change at line 24 skipping to change at line 24
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
) )
// Tree is the representation of a single parsed template. // Tree is the representation of a single parsed template.
type Tree struct { type Tree struct {
Name string // name of the template represented by the tree. Name string // name of the template represented by the tree.
ParseName string // name of the top-level template during parsing, for error messages. ParseName string // name of the top-level template during parsing, for error messages.
Root *ListNode // top-level root of the tree. Root *ListNode // top-level root of the tree.
Mode Mode // parsing mode.
text string // text parsed to create the template (or its parent) text string // text parsed to create the template (or its parent)
// Parsing only; cleared after parse. // Parsing only; cleared after parse.
funcs []map[string]interface{} funcs []map[string]interface{}
lex *lexer lex *lexer
token [3]item // three-token lookahead for parser. token [3]item // three-token lookahead for parser.
peekCount int peekCount int
vars []string // variables defined at the moment. vars []string // variables defined at the moment.
treeSet map[string]*Tree treeSet map[string]*Tree
actionLine int // line of left delim starting action
mode Mode
} }
// A mode value is a set of flags (or 0). Modes control parser behavior.
type Mode uint
const (
ParseComments Mode = 1 << iota // parse comments and add them to AST
)
// Copy returns a copy of the Tree. Any parsing state is discarded. // Copy returns a copy of the Tree. Any parsing state is discarded.
func (t *Tree) Copy() *Tree { func (t *Tree) Copy() *Tree {
if t == nil { if t == nil {
return nil return nil
} }
return &Tree{ return &Tree{
Name: t.Name, Name: t.Name,
ParseName: t.ParseName, ParseName: t.ParseName,
Root: t.Root.CopyList(), Root: t.Root.CopyList(),
text: t.text, text: t.text,
skipping to change at line 181 skipping to change at line 191
func (t *Tree) expectOneOf(expected1, expected2 itemType, context string) item { func (t *Tree) expectOneOf(expected1, expected2 itemType, context string) item {
token := t.nextNonSpace() token := t.nextNonSpace()
if token.typ != expected1 && token.typ != expected2 { if token.typ != expected1 && token.typ != expected2 {
t.unexpected(token, context) t.unexpected(token, context)
} }
return token return token
} }
// unexpected complains about the token and terminates processing. // unexpected complains about the token and terminates processing.
func (t *Tree) unexpected(token item, context string) { func (t *Tree) unexpected(token item, context string) {
if token.typ == itemError {
extra := ""
if t.actionLine != 0 && t.actionLine != token.line {
extra = fmt.Sprintf(" in action started at %s:%d", t.Pars
eName, t.actionLine)
if strings.HasSuffix(token.val, " action") {
extra = extra[len(" in action"):] // avoid "actio
n in action"
}
}
t.errorf("%s%s", token, extra)
}
t.errorf("unexpected %s in %s", token, context) t.errorf("unexpected %s in %s", token, context)
} }
// recover is the handler that turns panics into returns from the top level of P arse. // recover is the handler that turns panics into returns from the top level of P arse.
func (t *Tree) recover(errp *error) { func (t *Tree) recover(errp *error) {
e := recover() e := recover()
if e != nil { if e != nil {
if _, ok := e.(runtime.Error); ok { if _, ok := e.(runtime.Error); ok {
panic(e) panic(e)
} }
skipping to change at line 223 skipping to change at line 243
t.treeSet = nil t.treeSet = nil
} }
// Parse parses the template definition string to construct a representation of // Parse parses the template definition string to construct a representation of
// the template for execution. If either action delimiter string is empty, the // the template for execution. If either action delimiter string is empty, the
// default ("{{" or "}}") is used. Embedded template definitions are added to // default ("{{" or "}}") is used. Embedded template definitions are added to
// the treeSet map. // the treeSet map.
func (t *Tree) Parse(text, leftDelim, rightDelim string, treeSet map[string]*Tre e, funcs ...map[string]interface{}) (tree *Tree, err error) { func (t *Tree) Parse(text, leftDelim, rightDelim string, treeSet map[string]*Tre e, funcs ...map[string]interface{}) (tree *Tree, err error) {
defer t.recover(&err) defer t.recover(&err)
t.ParseName = t.Name t.ParseName = t.Name
t.startParse(funcs, lex(t.Name, text, leftDelim, rightDelim), treeSet) emitComment := t.Mode&ParseComments != 0
t.startParse(funcs, lex(t.Name, text, leftDelim, rightDelim, emitComment)
, treeSet)
t.text = text t.text = text
t.parse() t.parse()
t.add() t.add()
t.stopParse() t.stopParse()
return t, nil return t, nil
} }
// add adds tree to t.treeSet. // add adds tree to t.treeSet.
func (t *Tree) add() { func (t *Tree) add() {
tree := t.treeSet[t.Name] tree := t.treeSet[t.Name]
if tree == nil || IsEmptyTree(tree.Root) { if tree == nil || IsEmptyTree(tree.Root) {
t.treeSet[t.Name] = t t.treeSet[t.Name] = t
return return
} }
if !IsEmptyTree(t.Root) { if !IsEmptyTree(t.Root) {
t.errorf("template: multiple definition of template %q", t.Name) t.errorf("template: multiple definition of template %q", t.Name)
} }
} }
// IsEmptyTree reports whether this tree (node) is empty of everything but space . // IsEmptyTree reports whether this tree (node) is empty of everything but space or comments.
func IsEmptyTree(n Node) bool { func IsEmptyTree(n Node) bool {
switch n := n.(type) { switch n := n.(type) {
case nil: case nil:
return true return true
case *ActionNode: case *ActionNode:
case *CommentNode:
return true
case *IfNode: case *IfNode:
case *ListNode: case *ListNode:
for _, node := range n.Nodes { for _, node := range n.Nodes {
if !IsEmptyTree(node) { if !IsEmptyTree(node) {
return false return false
} }
} }
return true return true
case *RangeNode: case *RangeNode:
case *TemplateNode: case *TemplateNode:
skipping to change at line 279 skipping to change at line 302
// as itemList except it also parses {{define}} actions. // as itemList except it also parses {{define}} actions.
// It runs to EOF. // It runs to EOF.
func (t *Tree) parse() { func (t *Tree) parse() {
t.Root = t.newList(t.peek().pos) t.Root = t.newList(t.peek().pos)
for t.peek().typ != itemEOF { for t.peek().typ != itemEOF {
if t.peek().typ == itemLeftDelim { if t.peek().typ == itemLeftDelim {
delim := t.next() delim := t.next()
if t.nextNonSpace().typ == itemDefine { if t.nextNonSpace().typ == itemDefine {
newT := New("definition") // name will be updated once we know it. newT := New("definition") // name will be updated once we know it.
newT.text = t.text newT.text = t.text
newT.Mode = t.Mode
newT.ParseName = t.ParseName newT.ParseName = t.ParseName
newT.startParse(t.funcs, t.lex, t.treeSet) newT.startParse(t.funcs, t.lex, t.treeSet)
newT.parseDefinition() newT.parseDefinition()
continue continue
} }
t.backup2(delim) t.backup2(delim)
} }
switch n := t.textOrAction(); n.Type() { switch n := t.textOrAction(); n.Type() {
case nodeEnd, nodeElse: case nodeEnd, nodeElse:
t.errorf("unexpected %s", n) t.errorf("unexpected %s", n)
skipping to change at line 334 skipping to change at line 358
case nodeEnd, nodeElse: case nodeEnd, nodeElse:
return list, n return list, n
} }
list.append(n) list.append(n)
} }
t.errorf("unexpected EOF") t.errorf("unexpected EOF")
return return
} }
// textOrAction: // textOrAction:
// text | action // text | comment | action
func (t *Tree) textOrAction() Node { func (t *Tree) textOrAction() Node {
switch token := t.nextNonSpace(); token.typ { switch token := t.nextNonSpace(); token.typ {
case itemText: case itemText:
return t.newText(token.pos, token.val) return t.newText(token.pos, token.val)
case itemLeftDelim: case itemLeftDelim:
t.actionLine = token.line
defer t.clearActionLine()
return t.action() return t.action()
case itemComment:
return t.newComment(token.pos, token.val)
default: default:
t.unexpected(token, "input") t.unexpected(token, "input")
} }
return nil return nil
} }
func (t *Tree) clearActionLine() {
t.actionLine = 0
}
// Action: // Action:
// control // control
// command ("|" command)* // command ("|" command)*
// Left delim is past. Now get actions. // Left delim is past. Now get actions.
// First word could be a keyword such as range. // First word could be a keyword such as range.
func (t *Tree) action() (n Node) { func (t *Tree) action() (n Node) {
switch token := t.nextNonSpace(); token.typ { switch token := t.nextNonSpace(); token.typ {
case itemBlock: case itemBlock:
return t.blockControl() return t.blockControl()
case itemElse: case itemElse:
skipping to change at line 372 skipping to change at line 404
case itemRange: case itemRange:
return t.rangeControl() return t.rangeControl()
case itemTemplate: case itemTemplate:
return t.templateControl() return t.templateControl()
case itemWith: case itemWith:
return t.withControl() return t.withControl()
} }
t.backup() t.backup()
token := t.peek() token := t.peek()
// Do not pop variables; they persist until "end". // Do not pop variables; they persist until "end".
return t.newAction(token.pos, token.line, t.pipeline("command")) return t.newAction(token.pos, token.line, t.pipeline("command", itemRight Delim))
} }
// Pipeline: // Pipeline:
// declarations? command ('|' command)* // declarations? command ('|' command)*
func (t *Tree) pipeline(context string) (pipe *PipeNode) { func (t *Tree) pipeline(context string, end itemType) (pipe *PipeNode) {
token := t.peekNonSpace() token := t.peekNonSpace()
pipe = t.newPipeline(token.pos, token.line, nil) pipe = t.newPipeline(token.pos, token.line, nil)
// Are there declarations or assignments? // Are there declarations or assignments?
decls: decls:
if v := t.peekNonSpace(); v.typ == itemVariable { if v := t.peekNonSpace(); v.typ == itemVariable {
t.next() t.next()
// Since space is a token, we need 3-token look-ahead here in the worst case: // Since space is a token, we need 3-token look-ahead here in the worst case:
// in "$x foo" we need to read "foo" (as opposed to ":=") to know that $x is an // in "$x foo" we need to read "foo" (as opposed to ":=") to know that $x is an
// argument variable rather than a declaration. So remember the t oken // argument variable rather than a declaration. So remember the t oken
// adjacent to the variable so we can push it back if necessary. // adjacent to the variable so we can push it back if necessary.
skipping to change at line 418 skipping to change at line 450
} }
t.errorf("too many declarations in %s", context) t.errorf("too many declarations in %s", context)
case tokenAfterVariable.typ == itemSpace: case tokenAfterVariable.typ == itemSpace:
t.backup3(v, tokenAfterVariable) t.backup3(v, tokenAfterVariable)
default: default:
t.backup2(v) t.backup2(v)
} }
} }
for { for {
switch token := t.nextNonSpace(); token.typ { switch token := t.nextNonSpace(); token.typ {
case itemRightDelim, itemRightParen: case end:
// At this point, the pipeline is complete // At this point, the pipeline is complete
t.checkPipeline(pipe, context) t.checkPipeline(pipe, context)
if token.typ == itemRightParen {
t.backup()
}
return return
case itemBool, itemCharConstant, itemComplex, itemDot, itemField, itemIdentifier, case itemBool, itemCharConstant, itemComplex, itemDot, itemField, itemIdentifier,
itemNumber, itemNil, itemRawString, itemString, itemVaria ble, itemLeftParen: itemNumber, itemNil, itemRawString, itemString, itemVaria ble, itemLeftParen:
t.backup() t.backup()
pipe.append(t.command()) pipe.append(t.command())
default: default:
t.unexpected(token, context) t.unexpected(token, context)
} }
} }
} }
skipping to change at line 452 skipping to change at line 481
switch c.Args[0].Type() { switch c.Args[0].Type() {
case NodeBool, NodeDot, NodeNil, NodeNumber, NodeString: case NodeBool, NodeDot, NodeNil, NodeNumber, NodeString:
// With A|B|C, pipeline stage 2 is B // With A|B|C, pipeline stage 2 is B
t.errorf("non executable command in pipeline stage %d", i +2) t.errorf("non executable command in pipeline stage %d", i +2)
} }
} }
} }
func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int , pipe *PipeNode, list, elseList *ListNode) { func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int , pipe *PipeNode, list, elseList *ListNode) {
defer t.popVars(len(t.vars)) defer t.popVars(len(t.vars))
pipe = t.pipeline(context) pipe = t.pipeline(context, itemRightDelim)
var next Node var next Node
list, next = t.itemList() list, next = t.itemList()
switch next.Type() { switch next.Type() {
case nodeEnd: //done case nodeEnd: //done
case nodeElse: case nodeElse:
if allowElseIf { if allowElseIf {
// Special case for "else if". If the "else" is followed immediately by an "if", // Special case for "else if". If the "else" is followed immediately by an "if",
// the elseControl will have left the "if" token pending. Treat // the elseControl will have left the "if" token pending. Treat
// {{if a}}_{{else if b}}_{{end}} // {{if a}}_{{else if b}}_{{end}}
// as // as
skipping to change at line 538 skipping to change at line 567
// Block: // Block:
// {{block stringValue pipeline}} // {{block stringValue pipeline}}
// Block keyword is past. // Block keyword is past.
// The name must be something that can evaluate to a string. // The name must be something that can evaluate to a string.
// The pipeline is mandatory. // The pipeline is mandatory.
func (t *Tree) blockControl() Node { func (t *Tree) blockControl() Node {
const context = "block clause" const context = "block clause"
token := t.nextNonSpace() token := t.nextNonSpace()
name := t.parseTemplateName(token, context) name := t.parseTemplateName(token, context)
pipe := t.pipeline(context) pipe := t.pipeline(context, itemRightDelim)
block := New(name) // name will be updated once we know it. block := New(name) // name will be updated once we know it.
block.text = t.text block.text = t.text
block.Mode = t.Mode
block.ParseName = t.ParseName block.ParseName = t.ParseName
block.startParse(t.funcs, t.lex, t.treeSet) block.startParse(t.funcs, t.lex, t.treeSet)
var end Node var end Node
block.Root, end = block.itemList() block.Root, end = block.itemList()
if end.Type() != nodeEnd { if end.Type() != nodeEnd {
t.errorf("unexpected %s in %s", end, context) t.errorf("unexpected %s in %s", end, context)
} }
block.add() block.add()
block.stopParse() block.stopParse()
skipping to change at line 567 skipping to change at line 597
// Template keyword is past. The name must be something that can evaluate // Template keyword is past. The name must be something that can evaluate
// to a string. // to a string.
func (t *Tree) templateControl() Node { func (t *Tree) templateControl() Node {
const context = "template clause" const context = "template clause"
token := t.nextNonSpace() token := t.nextNonSpace()
name := t.parseTemplateName(token, context) name := t.parseTemplateName(token, context)
var pipe *PipeNode var pipe *PipeNode
if t.nextNonSpace().typ != itemRightDelim { if t.nextNonSpace().typ != itemRightDelim {
t.backup() t.backup()
// Do not pop variables; they persist until "end". // Do not pop variables; they persist until "end".
pipe = t.pipeline(context) pipe = t.pipeline(context, itemRightDelim)
} }
return t.newTemplate(token.pos, token.line, name, pipe) return t.newTemplate(token.pos, token.line, name, pipe)
} }
func (t *Tree) parseTemplateName(token item, context string) (name string) { func (t *Tree) parseTemplateName(token item, context string) (name string) {
switch token.typ { switch token.typ {
case itemString, itemRawString: case itemString, itemRawString:
s, err := strconv.Unquote(token.val) s, err := strconv.Unquote(token.val)
if err != nil { if err != nil {
t.error(err) t.error(err)
skipping to change at line 601 skipping to change at line 631
cmd := t.newCommand(t.peekNonSpace().pos) cmd := t.newCommand(t.peekNonSpace().pos)
for { for {
t.peekNonSpace() // skip leading spaces. t.peekNonSpace() // skip leading spaces.
operand := t.operand() operand := t.operand()
if operand != nil { if operand != nil {
cmd.append(operand) cmd.append(operand)
} }
switch token := t.next(); token.typ { switch token := t.next(); token.typ {
case itemSpace: case itemSpace:
continue continue
case itemError:
t.errorf("%s", token.val)
case itemRightDelim, itemRightParen: case itemRightDelim, itemRightParen:
t.backup() t.backup()
case itemPipe: case itemPipe:
// nothing here; break loop below
default: default:
t.errorf("unexpected %s in operand", token) t.unexpected(token, "operand")
} }
break break
} }
if len(cmd.Args) == 0 { if len(cmd.Args) == 0 {
t.errorf("empty command") t.errorf("empty command")
} }
return cmd return cmd
} }
// operand: // operand:
skipping to change at line 662 skipping to change at line 691
// literal (number, string, nil, boolean) // literal (number, string, nil, boolean)
// function (identifier) // function (identifier)
// . // .
// .Field // .Field
// $ // $
// '(' pipeline ')' // '(' pipeline ')'
// A term is a simple "expression". // A term is a simple "expression".
// A nil return means the next item is not a term. // A nil return means the next item is not a term.
func (t *Tree) term() Node { func (t *Tree) term() Node {
switch token := t.nextNonSpace(); token.typ { switch token := t.nextNonSpace(); token.typ {
case itemError:
t.errorf("%s", token.val)
case itemIdentifier: case itemIdentifier:
if !t.hasFunction(token.val) { if !t.hasFunction(token.val) {
t.errorf("function %q not defined", token.val) t.errorf("function %q not defined", token.val)
} }
return NewIdentifier(token.val).SetTree(t).SetPos(token.pos) return NewIdentifier(token.val).SetTree(t).SetPos(token.pos)
case itemDot: case itemDot:
return t.newDot(token.pos) return t.newDot(token.pos)
case itemNil: case itemNil:
return t.newNil(token.pos) return t.newNil(token.pos)
case itemVariable: case itemVariable:
skipping to change at line 686 skipping to change at line 713
return t.newField(token.pos, token.val) return t.newField(token.pos, token.val)
case itemBool: case itemBool:
return t.newBool(token.pos, token.val == "true") return t.newBool(token.pos, token.val == "true")
case itemCharConstant, itemComplex, itemNumber: case itemCharConstant, itemComplex, itemNumber:
number, err := t.newNumber(token.pos, token.val, token.typ) number, err := t.newNumber(token.pos, token.val, token.typ)
if err != nil { if err != nil {
t.error(err) t.error(err)
} }
return number return number
case itemLeftParen: case itemLeftParen:
pipe := t.pipeline("parenthesized pipeline") return t.pipeline("parenthesized pipeline", itemRightParen)
if token := t.next(); token.typ != itemRightParen {
t.errorf("unclosed right paren: unexpected %s", token)
}
return pipe
case itemString, itemRawString: case itemString, itemRawString:
s, err := strconv.Unquote(token.val) s, err := strconv.Unquote(token.val)
if err != nil { if err != nil {
t.error(err) t.error(err)
} }
return t.newString(token.pos, token.val, s) return t.newString(token.pos, token.val, s)
} }
t.backup() t.backup()
return nil return nil
} }
 End of changes. 25 change blocks. 
28 lines changed or deleted 54 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)