Go Tool学习(一)scanner(2)TestSemis方法
测试输入——全局变量lines:
var lines = []string{
// # indicates a semicolon present in the source
// $ indicates an automatically inserted semicolon
"",
"\ufeff#;", // first BOM is ignored
"#;",
"foo$\n",
"123$\n",
...
TestSemis方法很简单,对lines中的每一个元素都会创建多个scanner去scan,创建scanner的操作再checkSmi方法中。
func TestSemis(t *testing.T) {
for _, line := range lines {
checkSemi(t, line, 0)
checkSemi(t, line, ScanComments) // 不忽略注释
// 如果输入以换行符结束,那么去掉换行符的输入进行checkSemi应该也
// 能通过。所以这里将换行符去掉后再进行checkSemi
for i := len(line) - 1; i >= 0 && line[i] == '\n'; i-- {
checkSemi(t, line[0:i], 0)
checkSemi(t, line[0:i], ScanComments)
}
}
}
checkSemi:
首先初始化一个scanner
var S Scanner
file := fset.AddFile("TestSemis", fset.Base(), len(line))
S.Init(file, []byte(line), nil, mode)
这里提一下fset的base,看了一下fset.AddFile实现,每次调用后都会将fset.base改为fset.base + size + 1
,这里的size即文件长度。
进行check
for tok != token.EOF {
if tok == token.ILLEGAL {
// illegal token即无法识别的token,checkSemi主要的逻辑就在这里
// 主要两种情况:
// 1.illegal token是'#',那么semiLit(semicolon literal,“分号”序列)应该是';'
// 2.否则semiLit应该是'\n'
// 至于为什么是这样,还不清楚 <_<
// 还有一点要提一下:前面有说如果是'\n'结束的输入删去'\n'后也要能通过checkSemi,
// 主要是因为scanner会自动插入semicolon,scanner有个属性insertSemi默认false,即
// 默认不会自动插入,但是在scanner.Scan方法625行可以看到,比如如果当前token是一个
// 数字,scanner会把insertSemi设置为true,在637行发现当前token是'\n'时又会把insertSemi
// 设置为false
semiLit := "\n"
if lit[0] == '#' {
semiLit = ";"
}
// illegal token的后一个token一定是一个semicolon
// 所以先对semiPos的Offset和Column都加1
// 有可能semiLit是'\n'的情况,但这里并没有对semiPos.Line加1的处理,这说明'\n'字符
// 本身应该是属于当前的,其后续字符是属于下一行的(总不能是程序bug吧<_<)
semiPos := file.Position(pos)
semiPos.Offset++
semiPos.Column++
pos, tok, lit = S.Scan() // 读下一个token,然后check
if tok == token.SEMICOLON {
if lit != semiLit {
t.Errorf(`bad literal for %q: got %q, expected %q`, line, lit, semiLit)
}
checkPos(t, line, pos, semiPos)
} else {
t.Errorf("bad token for %q: got %s, expected ;", line, tok)
}
} else if tok == token.SEMICOLON {
// 这里的条件判断,意味着只有illegal token后能够跟SEMICOLON Token
t.Errorf("bad token for %q: got ;, expected no ;", line)
}
// 其他token都被忽略了
pos, tok, lit = S.Scan()
}