文件组成 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