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