前文 Go 源碼閱讀筆記 text/template/parse 分析瞭解析要點node
注:標題 text/template/parse 最後的 parse 不是指一個包,或者文件名,由於代碼中各類交叉調用封裝,因此直接用了 parse 這個詞,本文標題中的 exec 也是這樣less
相關文件:ide
執行一個 template ,執行的入口在 exec.go函數
<!-- lang: cpp --> func (s *state) walk(dot reflect.Value, node parse.Node)
walk老是由Tree.Root開始,按照前文所述兩大類 pipeline類對應 ActionNode,流程控制類對應 IfNode,ListNode,RangeNode,TemplateNode,TextNode,WithNode,分別進行處理 若是 walk 中發生了錯誤,errRecover處理行爲是lua
<!-- lang: cpp --> func errRecover(errp *error) { e := recover() if e != nil { switch err := e.(type) { case runtime.Error: panic(e) case error: *errp = err default: panic(e) } } }
而對於 ActionNode 就是一個求值的過程,walk 方法中對於 ActionNode 的處理,代碼明確寫着 <!-- lang: cpp --> case *parse.ActionNode: val := s.evalPipeline(dot, node.Pipe) if len(node.Pipe.Decl) == 0 { s.printValue(node, val) }idea
求值入口是 evalPipeline.net
前文分析過template目前的實現全部的 ActionNode 都是封裝成 NodeCommand,因此 evalPipeline 方法中對ActionNode的求值變成 evalCommand,這樣繞了一圈,evalCommand是現階段 template 實現最終的 ActionNode 求值分派主體,NodeCommand 的 Args屬性封裝了真正的節點,而對應不一樣的節點類型必須寫對應的求值方法code
<!-- lang: cpp --> func (s *state) evalCommand(dot reflect.Value, cmd *parse.CommandNode, final reflect.Value) reflect.Value { firstWord := cmd.Args[0] switch n := firstWord.(type) { case *parse.FieldNode: return s.evalFieldNode(dot, n, cmd.Args, final) case *parse.ChainNode: return s.evalChainNode(dot, n, cmd.Args, final) case *parse.IdentifierNode: // Must be a function. return s.evalFunction(dot, n, cmd, cmd.Args, final) case *parse.PipeNode: // Parenthesized pipeline. The arguments are all inside the pipeline; final is ignored. return s.evalPipeline(dot, n) case *parse.VariableNode: return s.evalVariableNode(dot, n, cmd.Args, final) } s.at(firstWord) s.notAFunction(cmd.Args, final) switch word := firstWord.(type) { case *parse.BoolNode: return reflect.ValueOf(word.True) case *parse.DotNode: return dot case *parse.NilNode: s.errorf("nil is not a command") case *parse.NumberNode: return s.idealConstant(word) case *parse.StringNode: return reflect.ValueOf(word.Text) } s.errorf("can't evaluate command %q", firstWord) panic("not reached") }
以 FieldNode 節點爲例,最終的求值執行方法是blog
<!-- lang: cpp --> func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, args []parse.Node, final, receiver reflect.Value) reflect.Value { if !receiver.IsValid() { return zero } typ := receiver.Type() receiver, _ = indirect(receiver) // Unless it's an interface, need to get to a value of type *T to guarantee // we see all methods of T and *T. ptr := receiver if ptr.Kind() != reflect.Interface && ptr.CanAddr() { ptr = ptr.Addr() } if method := ptr.MethodByName(fieldName); method.IsValid() { return s.evalCall(dot, method, node, fieldName, args, final) } hasArgs := len(args) > 1 || final.IsValid() // It's not a method; must be a field of a struct or an element of a map. The receiver must not be nil. receiver, isNil := indirect(receiver) if isNil { s.errorf("nil pointer evaluating %s.%s", typ, fieldName) } switch receiver.Kind() { case reflect.Struct: tField, ok := receiver.Type().FieldByName(fieldName) if ok { field := receiver.FieldByIndex(tField.Index) if tField.PkgPath != "" { // field is unexported s.errorf("%s is an unexported field of struct type %s", fieldName, typ) } // If it's a function, we must call it. if hasArgs { s.errorf("%s has arguments but cannot be invoked as function", fieldName) } return field } s.errorf("%s is not a field of struct type %s", fieldName, typ) case reflect.Map: // If it's a map, attempt to use the field name as a key. nameVal := reflect.ValueOf(fieldName) if nameVal.Type().AssignableTo(receiver.Type().Key()) { if hasArgs { s.errorf("%s is not a method but has arguments", fieldName) } return receiver.MapIndex(nameVal) } } s.errorf("can't evaluate field %s in type %s", fieldName, typ) panic("not reached") }
看到這裏,路線清晰了,從外到內 流程控制類:包含 IfNode,ListNode,RangeNode,TemplateNode,TextNode,WithNode都是獨立的 。 ActionNode求值:包含 funcMap,內建函數, .Field,所有用NodeCommand進行封裝 全部的節點類型一定有對應的邏輯控制代碼或者求值代碼實現遞歸
解析模板並依靠 itemType 生成節點 Tree.term(), Tree.pipeline和Tree.operandd 根據 itemType類型判斷是否生成 NodeCommand.
感覺:筆者沒有本身完成一個遞歸向下lex的經驗,感受,go的template實現是一個務實的實現,所謂務實是指不糾結於嚴格的邏輯分類,整體看上去,層層的封裝一點都沒有少,猜想是爲之後的擴展打基礎,那些不嚴格的邏輯分類(或者說成是偷懶)估計只是在現階段暫時性的。實現一個lex,究竟是會走向效率仍是走向嚴格邏輯,看完template的實現,筆者也比較迷惑