Go 源碼閱讀筆記 text/template/parse


文件組成 node

  1. lex.go       詞法定義與解析
  2. node.go     node 定義與建立
  3. parse.go    生成 template 語法樹 tree

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 負責查找 遞歸

  • itemLeftDelim 默認"{{",能夠自定義

把處理過程交給 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.
前面說過lex分析的時候分只有兩種狀況 lexText , lexInsideAction ,所以 parse 中
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 的節點分兩大類

  1. ActionNode,其實封裝了各類pipline的識別信息,
  2. xxxxControl,流程控制類,IfNode,RangeNode,WithNode這些
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

相關文章
相關標籤/搜索