文件組成 node
lex.go 要點 app
itemType 常量表次序規則:itemKeyword 用來分界詞法中的關鍵字和其餘詞法元素,參見下列實現 ide
func (i item) String() string
func lexIdentifier(l *lexer) stateFn
node.go 要點 函數
各類 NodeType 要實現 Node 接口 this
type Node interface { Type() NodeType String() string Copy() Node Position() Pos unexported() }
parse.go 要點 spa
每一個模板都有惟一的模板名,並對應生成一個 Tree 設計
type Tree struct { Name string ParseName string Root *ListNode text string funcs []map[string]interface{} lex *lexer token [3]item peekCount int vars []string }
Tree 的 Parse 方法中 defer t.recover(&err) 捕獲解析過程當中發生的 error ,若是是 runtime.Error 就 panic,不然 stopParse
Parse方法首先進行的是詞法分析(內部使用goroutine和chan並行完成,其實和節點的生成是同步的)
code
t.startParse(funcs, lex(t.Name, text, leftDelim, rightDelim))lex.go 中的 lexer定義
type lexer struct { name string // the name of the input; used only for error reports input string // the string being scanned leftDelim string // start of action rightDelim string // end of action state stateFn // the next lexing function to enter pos Pos // current position in the input start Pos // start position of this item width Pos // width of last rune read from input lastPos Pos // position of most recent item returned by nextItem items chan item // channel of scanned items parenDepth int // nesting depth of ( ) exprs }
lexer在解析過程當中的順序老是從一個 lexText 開始,遍歷 input 。lexText 負責查找 遞歸
把處理過程交給 func lexLeftDelim,lexLeftDelim 負責查找註釋和非註釋(被稱作lexInsideAction),處理過程交給 func lexComment,lexComment 解析到註釋結束後,又把處理過程交給lexText,進行 lexInsideAction 解析。 token
lexInsideAction 要點
全部定義的詞法都在這個函數中處理,根據詞法定義交給具體的詞法處理函數,造成詞法處理鏈,完成遍歷。每個詞法處理函數負責檢查詞法有效性。這個過程僅僅是一個詞法處理鏈,遍歷 input
某個詞法分析經過後,會生成對應的節點,私有方法 parse 完成此過程。
t.parse(treeSet)第一個節點 Tree.Root 要點
type ListNode struct { NodeType Pos Nodes []Node // The element nodes in lexical order. }Root 是一個 ListNode ,保存 Tree 解析全部的 Node.
n := t.textOrAction()這是全部的節點生成的入口點
func (t *Tree) textOrAction() Node { switch token := t.nextNonSpace(); token.typ { case itemText: return newText(token.pos, token.val) case itemLeftDelim: return t.action() //這裏全部{{...}}中的代碼都由action處理 default: t.unexpected(token, "input") } return nil }
func (t *Tree) action() (n Node) { switch token := t.nextNonSpace(); token.typ { case itemElse: return t.elseControl() case itemEnd: return t.endControl() case itemIf: return t.ifControl() case itemRange: return t.rangeControl() case itemTemplate: return t.templateControl() case itemWith: return t.withControl() } t.backup() // Do not pop variables; they persist until "end". return newAction(t.peek().pos, t.lex.lineNumber(), t.pipeline("command")) }而action方法把模板中的關鍵字劃分爲 xxxControl 處理,其餘都由 newAction 生成 ActionNode ,
也能夠理解爲:template 的節點分兩大類
return newAction(t.peek().pos, t.lex.lineNumber(), t.pipeline("command"))這裏面的要點:
t.pipeline("command")
看pipeline的實現,其實全部的 pipeline 節點都都封裝爲 NodeCommand, An element of a pipeline
pipeline 方法中對全部已知的 action 進行處理,閱讀代碼發現 template 的語法設計中使用這種pipeline的方式,使得解析器只須要向下分析,也就是所謂的 peek ,不須要回退,這種設計無疑簡化了分析器,我不知道對之後的擴展是否會有所限制
從 Parse->parse->textOrAction->pipeline 遞歸向下生成了全部的節點,而最終 parse 中 t.Root.append(n) 把全部生成的節點所有添加到 Tree.Root
從struct來講,通過了一層層的封裝 lex.go itemType -> node.go NodeType->node.go XxxxNode 進入Tree.Root.Nodes