Golang Template source code analysis(Parse)


    This blog was written at go 1.3.1 version.

    We know that we use template thought by followed way:

func main() {
	name := "waynehu"
	tmpl := template.New("test")
	tmpl, err := tmpl.Parse("hello {{.}}")

	if err != nil {
		panic(err)
	}

	err = tmpl.Execute(os.Stdout, name)
	if err != nil {
		panic(err)
	}
}
    It mainly has three steps,the first is allow template,the second is tmpl.Parse,the third is tmpl.Execute(os.Stdout, name),the first step is simple.This blog introduces the second step.

  •     Major Class

    The ListNode struct:

// ListNode holds a sequence of nodes.
type ListNode struct {
	NodeType
	Pos
	Nodes []Node // The element nodes in lexical order.
}
    The "NodeType" and "Pos" is as same as int.

  • Sequence Diagram


  • The key of parts of code
// lex creates a new scanner for the input string.
func lex(name, input, left, right string) *lexer {
	if left == "" {
		left = leftDelim
	}
	if right == "" {
		right = rightDelim
	}
	l := &lexer{
		name:       name,
		input:      input,
		leftDelim:  left,
		rightDelim: right,
		items:      make(chan item),
	}
	go l.run()
	return l
}



// run runs the state machine for the lexer.
func (l *lexer) run() {
    for l.state = lexText; l.state != nil; {
        l.state = l.state(l)
    }
}

l.emit(itemText) is handle static text information and lexLeftDelim is handle dynamic information.

// lexText scans until an opening action delimiter, "{{".
func lexText(l *lexer) stateFn {
	for {
		if strings.HasPrefix(l.input[l.pos:], l.leftDelim) {
			if l.pos > l.start {
				l.emit(itemText)
			}
			return lexLeftDelim
		}
		if l.next() == eof {
			break
		}
	}
	// Correctly reached EOF.
	if l.pos > l.start {
		l.emit(itemText)
	}
	l.emit(itemEOF)
	return nil
}

lexComment is hande comment without analysis

// lexLeftDelim scans the left delimiter, which is known to be present.
func lexLeftDelim(l *lexer) stateFn {
	l.pos += Pos(len(l.leftDelim))
	if strings.HasPrefix(l.input[l.pos:], leftComment) {
		return lexComment
	}
	l.emit(itemLeftDelim)
	l.parenDepth = 0
	return lexInsideAction
}

any syntax parsing are transmited by "chan item" is a channel.and take away it thought func (l *lexer) nextItem() item function.

put value in emit function.

// emit passes an item back to the client.
func (l *lexer) emit(t itemType) {
	l.items <- item{t, l.start, l.input[l.start:l.pos]}
	l.start = l.pos
}

followed function produce t.Root list thought nextNonSpace bring value to

// parse is the top-level parser for a template, essentially the same
// as itemList except it also parses {{define}} actions.
// It runs to EOF.
func (t *Tree) parse(treeSet map[string]*Tree) (next Node) {
	t.Root = newList(t.peek().pos)
	for t.peek().typ != itemEOF {
		if t.peek().typ == itemLeftDelim {
			delim := t.next()
			if t.nextNonSpace().typ == itemDefine {
				newT := New("definition") // name will be updated once we know it.
				newT.text = t.text
				newT.ParseName = t.ParseName
				newT.startParse(t.funcs, t.lex)
				newT.parseDefinition(treeSet)
				continue
			}
			t.backup2(delim)
		}
		n := t.textOrAction()
		if n.Type() == nodeEnd {
			t.errorf("unexpected %s", n)
		}
		t.Root.append(n)
	}
	return nil
}

// nextNonSpace returns the next non-space token.
func (t *Tree) nextNonSpace() (token item) {
	for {
		token = t.next()
		if token.typ != itemSpace {
			break
		}
	}
	return token
}


take away item in nextItem function

// nextItem returns the next item from the input.
func (l *lexer) nextItem() item {
	item := <-l.items
	l.lastPos = item.pos
	return item
}

To be continued

原文地址:https://www.cnblogs.com/liguangsunls/p/7220658.html