mirror of
https://github.com/gohugoio/hugo.git
synced 2025-08-25 22:00:58 +02:00
tpl/internal: Synch Go templates fork with Go 1.16dev
This commit is contained in:
@@ -21,16 +21,26 @@ type Tree struct {
|
||||
Name string // name of the template represented by the tree.
|
||||
ParseName string // name of the top-level template during parsing, for error messages.
|
||||
Root *ListNode // top-level root of the tree.
|
||||
Mode Mode // parsing mode.
|
||||
text string // text parsed to create the template (or its parent)
|
||||
// Parsing only; cleared after parse.
|
||||
funcs []map[string]interface{}
|
||||
lex *lexer
|
||||
token [3]item // three-token lookahead for parser.
|
||||
peekCount int
|
||||
vars []string // variables defined at the moment.
|
||||
treeSet map[string]*Tree
|
||||
funcs []map[string]interface{}
|
||||
lex *lexer
|
||||
token [3]item // three-token lookahead for parser.
|
||||
peekCount int
|
||||
vars []string // variables defined at the moment.
|
||||
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.
|
||||
func (t *Tree) Copy() *Tree {
|
||||
if t == nil {
|
||||
@@ -178,6 +188,16 @@ func (t *Tree) expectOneOf(expected1, expected2 itemType, context string) item {
|
||||
|
||||
// unexpected complains about the token and terminates processing.
|
||||
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.ParseName, t.actionLine)
|
||||
if strings.HasSuffix(token.val, " action") {
|
||||
extra = extra[len(" in action"):] // avoid "action in action"
|
||||
}
|
||||
}
|
||||
t.errorf("%s%s", token, extra)
|
||||
}
|
||||
t.errorf("unexpected %s in %s", token, context)
|
||||
}
|
||||
|
||||
@@ -220,7 +240,8 @@ func (t *Tree) stopParse() {
|
||||
func (t *Tree) Parse(text, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]interface{}) (tree *Tree, err error) {
|
||||
defer t.recover(&err)
|
||||
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.parse()
|
||||
t.add()
|
||||
@@ -240,12 +261,14 @@ func (t *Tree) add() {
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
switch n := n.(type) {
|
||||
case nil:
|
||||
return true
|
||||
case *ActionNode:
|
||||
case *CommentNode:
|
||||
return true
|
||||
case *IfNode:
|
||||
case *ListNode:
|
||||
for _, node := range n.Nodes {
|
||||
@@ -276,6 +299,7 @@ func (t *Tree) parse() {
|
||||
if t.nextNonSpace().typ == itemDefine {
|
||||
newT := New("definition") // name will be updated once we know it.
|
||||
newT.text = t.text
|
||||
newT.Mode = t.Mode
|
||||
newT.ParseName = t.ParseName
|
||||
newT.startParse(t.funcs, t.lex, t.treeSet)
|
||||
newT.parseDefinition()
|
||||
@@ -331,19 +355,27 @@ func (t *Tree) itemList() (list *ListNode, next Node) {
|
||||
}
|
||||
|
||||
// textOrAction:
|
||||
// text | action
|
||||
// text | comment | action
|
||||
func (t *Tree) textOrAction() Node {
|
||||
switch token := t.nextNonSpace(); token.typ {
|
||||
case itemText:
|
||||
return t.newText(token.pos, token.val)
|
||||
case itemLeftDelim:
|
||||
t.actionLine = token.line
|
||||
defer t.clearActionLine()
|
||||
return t.action()
|
||||
case itemComment:
|
||||
return t.newComment(token.pos, token.val)
|
||||
default:
|
||||
t.unexpected(token, "input")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Tree) clearActionLine() {
|
||||
t.actionLine = 0
|
||||
}
|
||||
|
||||
// Action:
|
||||
// control
|
||||
// command ("|" command)*
|
||||
@@ -369,12 +401,12 @@ func (t *Tree) action() (n Node) {
|
||||
t.backup()
|
||||
token := t.peek()
|
||||
// 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", itemRightDelim))
|
||||
}
|
||||
|
||||
// Pipeline:
|
||||
// declarations? command ('|' command)*
|
||||
func (t *Tree) pipeline(context string) (pipe *PipeNode) {
|
||||
func (t *Tree) pipeline(context string, end itemType) (pipe *PipeNode) {
|
||||
token := t.peekNonSpace()
|
||||
pipe = t.newPipeline(token.pos, token.line, nil)
|
||||
// Are there declarations or assignments?
|
||||
@@ -415,12 +447,9 @@ decls:
|
||||
}
|
||||
for {
|
||||
switch token := t.nextNonSpace(); token.typ {
|
||||
case itemRightDelim, itemRightParen:
|
||||
case end:
|
||||
// At this point, the pipeline is complete
|
||||
t.checkPipeline(pipe, context)
|
||||
if token.typ == itemRightParen {
|
||||
t.backup()
|
||||
}
|
||||
return
|
||||
case itemBool, itemCharConstant, itemComplex, itemDot, itemField, itemIdentifier,
|
||||
itemNumber, itemNil, itemRawString, itemString, itemVariable, itemLeftParen:
|
||||
@@ -449,7 +478,7 @@ func (t *Tree) checkPipeline(pipe *PipeNode, context string) {
|
||||
|
||||
func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) {
|
||||
defer t.popVars(len(t.vars))
|
||||
pipe = t.pipeline(context)
|
||||
pipe = t.pipeline(context, itemRightDelim)
|
||||
var next Node
|
||||
list, next = t.itemList()
|
||||
switch next.Type() {
|
||||
@@ -535,10 +564,11 @@ func (t *Tree) blockControl() Node {
|
||||
|
||||
token := t.nextNonSpace()
|
||||
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.text = t.text
|
||||
block.Mode = t.Mode
|
||||
block.ParseName = t.ParseName
|
||||
block.startParse(t.funcs, t.lex, t.treeSet)
|
||||
var end Node
|
||||
@@ -564,7 +594,7 @@ func (t *Tree) templateControl() Node {
|
||||
if t.nextNonSpace().typ != itemRightDelim {
|
||||
t.backup()
|
||||
// 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)
|
||||
}
|
||||
@@ -598,13 +628,12 @@ func (t *Tree) command() *CommandNode {
|
||||
switch token := t.next(); token.typ {
|
||||
case itemSpace:
|
||||
continue
|
||||
case itemError:
|
||||
t.errorf("%s", token.val)
|
||||
case itemRightDelim, itemRightParen:
|
||||
t.backup()
|
||||
case itemPipe:
|
||||
// nothing here; break loop below
|
||||
default:
|
||||
t.errorf("unexpected %s in operand", token)
|
||||
t.unexpected(token, "operand")
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -659,8 +688,6 @@ func (t *Tree) operand() Node {
|
||||
// A nil return means the next item is not a term.
|
||||
func (t *Tree) term() Node {
|
||||
switch token := t.nextNonSpace(); token.typ {
|
||||
case itemError:
|
||||
t.errorf("%s", token.val)
|
||||
case itemIdentifier:
|
||||
if !t.hasFunction(token.val) {
|
||||
t.errorf("function %q not defined", token.val)
|
||||
@@ -683,11 +710,7 @@ func (t *Tree) term() Node {
|
||||
}
|
||||
return number
|
||||
case itemLeftParen:
|
||||
pipe := t.pipeline("parenthesized pipeline")
|
||||
if token := t.next(); token.typ != itemRightParen {
|
||||
t.errorf("unclosed right paren: unexpected %s", token)
|
||||
}
|
||||
return pipe
|
||||
return t.pipeline("parenthesized pipeline", itemRightParen)
|
||||
case itemString, itemRawString:
|
||||
s, err := strconv.Unquote(token.val)
|
||||
if err != nil {
|
||||
|
Reference in New Issue
Block a user