使用Participle解析遇Unexpected Token错误及结果为空问题
使用Participle解析.htaccess时遇到的Unexpected Token及解析结果为空问题
问题背景
我在学习用Participle解析.htaccess规则时,碰到了Unexpected Token错误,无法定位原因。原代码如下:
// nolint: golint, dupl package main import ( "fmt" "io" "github.com/alecthomas/participle/v2" "github.com/alecthomas/participle/v2/lexer" ) var htaccessLexer = lexer.MustSimple([]lexer.SimpleRule{ {"Comment", `^#[^\n]*`}, {"Ident", `^\w+`}, {"Int", `\d+`}, {"String", `("(\\"|[^"])*"|\S+)`}, {"EOL", `[\n\r]+`}, {"whitespace", `[ \t]+`}, }) type HTACCESS struct { Directives []*Directive `@@*` } type Directive struct { Pos lexer.Position ErrorDocument *ErrorDocument `@@` } type ErrorDocument struct { Code int `"ErrorDocument" @Int` Path string `@String` } var htaccessParser = participle.MustBuild[HTACCESS]( participle.Lexer(htaccessLexer), participle.CaseInsensitive("Ident"), participle.Unquote("String"), participle.Elide("whitespace"), ) func Parse(r io.Reader) (*HTACCESS, error) { program, err := htaccessParser.Parse("", r) if err != nil { return nil, err } return program, nil } func main() { v, err := htaccessParser.ParseString("", `ErrorDocument 403 test`) if err != nil { panic(err) } fmt.Println(v) }
我认为代码逻辑没问题,预期能识别到403,但实际无法解析。
编辑补充
后来我修改了Lexer规则,代码如下:
var htaccessLexer = lexer.MustSimple([]lexer.SimpleRule{ {"dir", `^\w+`}, {"int", `\d+`}, {"str", `("(\\"|[^"])*"|\S+)`}, {"EOL", `[\n\r]+`}, {"whitespace", `\s+`}, })
修改后Unexpected Token错误消失,但解析结果仍为空数组,我不清楚修改Lexer规则解决原错误的原因,也不知道解析为空的问题出在哪。
问题原因分析
1. 原Lexer导致Unexpected Token的原因
- 行首锚点
^误用:Participle的SimpleLexer是逐字符扫描匹配的,不是按行处理。原规则里Comment和Ident的^会强制要求匹配行首位置,当扫描到非行首的字符时,这些规则会匹配失败,后续规则无法正常触发,最终抛出Unexpected Token错误。 - 空白规则范围不足:原
whitespace仅匹配[ \t]+,未覆盖所有空白类型,但核心问题还是锚点的误用。
2. 修改Lexer后解析结果为空的原因
修改后的Lexer把规则名改成了dir、int、str,但结构体标签里依然使用@Int、@String来指定token类型。Participle是通过规则名称来匹配token的,两者名称不对应,导致解析器无法识别对应token,最终返回空数组。
解决方案
- 修复原Lexer的锚点问题:去掉规则中的
^,让SimpleLexer从当前扫描位置正常匹配:
var htaccessLexer = lexer.MustSimple([]lexer.SimpleRule{ {"Comment", `#[^\n]*`}, {"Ident", `\w+`}, {"Int", `\d+`}, {"String", `("(\\"|[^"])*"|\S+)`}, {"EOL", `[\n\r]+`}, {"whitespace", `[ \t]+`}, })
- 保持规则名与结构体标签一致:如果要修改规则名,需同步修改结构体中的标签(比如把
@Int改成@int),或者直接保留原规则名Ident、Int、String。
修正后的代码运行后,就能正确解析出ErrorDocument的Code和Path。
内容的提问来源于stack exchange,提问作者Krum110487
相关产品推荐
相关产品推荐

