我正在参加「掘金·启航计划」
在上一章Action Block词法解析器中,我们看到了Hugo解析模板的详细步骤:
思路很清晰,当正在解析的字符符合某项要求时,交给具体的对象进行处理。 像Text, LeftDelim等。 这就让要我们想到了有限状态机FSM - Finite State Machine。 实际上Hugo也是用state来命名这些处理函数的。
我们顺着这个视角来看看样例中,生成的tokens之间的关系:
从层级关系来看,可以清晰的看到text和action是一个层,在action中的元素是另一层。 除了action之外,都会识别token并提交token。 而action的主要作用是识别下一个token,并转交给具体的处理函数进行处理。
这样一来,我们发现,每个处理函数,对应着不同的分析状态。 其中有的状态会提交token,有的状态只负责识别下一个可能的token。 从状态切换的视角来看,很像是FSM - Finite State Machine的工作模式。
从上面的公式中可以看出。 输入信息是当前状态加上事件。 输出信息是处理动作加上新的状态。
当前状态对应的是相应的处理函数,而事件则是识别到的字符信息。 与状态对应的数据就是位置信息和行信息。 处理动作就是函数中所做出的下一个状态决定。 同时也生成了新的数据,像新的位置信息和新地行信息。
我们结合FSM的概念再梳理一下可能的实现方法:
初始状态就是Text,初始数据就是样例模板片断。 触发动作后,识别出下一个状态是Left Delim,同时数据更新为action block相应的信息。 而触发事件就是当有请求想要获取下一个token - nextToken的时候。 这样我们就可以依据FSM的理念,同样实现一个action block模板的词法分析器了。
动手实践 - Show Me the Code of FSM #
package main
import (
"errors"
"fmt"
"github.com/sunwei/gobyexample/modules/fsm"
)
func main() {
// initial fsm with init state and data
f := fsm.New(firstState, &data{
err: nil,
raw: "first",
})
// add state with handler
f.Add(firstState,
func(event fsm.Event) (fsm.State, fsm.Data) {
if event.Type() == fsm.Action {
fmt.Println(event.Data().Raw())
}
return secondState, &data{
err: nil,
raw: "second",
}
})
f.Add(secondState,
func(event fsm.Event) (fsm.State, fsm.Data) {
if event.Type() == fsm.Action {
fmt.Println(event.Data().Raw())
}
return lastState, &data{
err: errors.New("something wrong"),
raw: "last",
}
})
// error occurs
f.Add(lastState,
func(event fsm.Event) (fsm.State, fsm.Data) {
if e := event.Data().Error(); e != nil {
fmt.Println(e)
return errorState, nil
}
// if there is no error
// quite with eof state
return eofState, &data{
err: nil,
raw: "",
}
})
for {
// send message to notify fsm start the processing
e := f.Process("continue")
// quit with error
if e != nil {
fmt.Println("break because of error")
break
}
// quite for eof state
if f.State() == eofState {
fmt.Println("eof")
break
}
}
}
const (
firstState = "first"
secondState = "second"
lastState = "last"
errorState = "error"
eofState = "eof"
)
type data struct {
err error
raw any
}
func (d *data) Error() error {
return d.err
}
func (d *data) Raw() any {
return d.raw
}
样例输出:
# FSM example
first
second
break because of error
Program exited.