You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

143 lines
3.3 KiB
Go

package cmd
import (
"database/sql"
"git.noahlan.cn/northlan/ntools-go/stringn/ac"
"strings"
)
type (
Match struct {
Prefix string // 匹配前缀
Content []rune // 内容
}
CMD struct {
IsCMD bool // 是否CMD
Matches []Match // 匹配项
}
Parser struct {
ac ac.AhoCorasick // ac自动机
distinct bool // 命令是否去重(总开关)
patterns []Pattern // 匹配规则
}
Pattern struct {
Prefix string // 前缀
Alias []string // 前缀别名
ContentMaxLen int // 内容最大长度,以rune数组长度计算
Distinct sql.NullBool // Distinct 命令是否去重 单个权重大于总开关
isAlias bool // alias
realPattern *Pattern // 作为Alias时指向的实际Pattern
}
)
func NewCMDParser(patterns ...Pattern) *Parser {
p := &Parser{}
keyPrefixArr := p.initPattern(patterns)
builder := ac.NewAhoCorasickBuilder(ac.Opts{
AsciiCaseInsensitive: true,
MatchOnlyWholeWords: false,
MatchKind: ac.LeftMostLongestMatch,
DFA: true,
})
p.ac = builder.Build(keyPrefixArr)
return p
}
func (p *Parser) initPattern(patterns []Pattern) []string {
p.patterns = make([]Pattern, 0, len(patterns))
result := make([]string, 0, len(patterns))
for _, pattern := range patterns {
p.patterns = append(p.patterns, pattern)
result = append(result, pattern.Prefix)
for _, alias := range pattern.Alias {
p.patterns = append(p.patterns, Pattern{
Prefix: alias,
isAlias: true,
realPattern: &Pattern{
Prefix: pattern.Prefix,
Alias: pattern.Alias,
ContentMaxLen: pattern.ContentMaxLen,
Distinct: pattern.Distinct,
},
})
result = append(result, alias)
}
}
return result
}
func (p *Parser) ParseTest(content string) []ac.Match {
return p.ac.FindAll(content)
}
// SetDistinct 设置命令去重
func (p *Parser) SetDistinct(distinct bool) {
p.distinct = distinct
}
// Parse 从弹幕内容解析命令
func (p *Parser) Parse(msg string) CMD {
// 移除多余空格
tmpContent := strings.TrimSpace(msg)
resp := CMD{
Matches: make([]Match, 0),
}
iter := p.ac.Iter(tmpContent)
for match := iter.Next(); match != nil; {
resp.IsCMD = true
pattern := p.patterns[match.Pattern()]
if pattern.isAlias {
pattern = *pattern.realPattern
}
m := Match{
Prefix: pattern.Prefix,
}
tmpNext := iter.Next()
// 避免同类型指令重复
if pattern.Distinct.Valid && pattern.Distinct.Bool {
for _, m := range resp.Matches {
if m.Prefix == pattern.Prefix {
goto end // continue out for
}
}
}
if p.distinct {
if !pattern.Distinct.Valid || pattern.Distinct.Bool {
for _, m := range resp.Matches {
if m.Prefix == pattern.Prefix {
goto end // continue out for
}
}
}
}
if tmpNext != nil {
//nextPattern := p.patterns[tmpNext.Pattern()]
content := []rune(tmpContent[match.End():tmpNext.Start()])
if len(content) > pattern.ContentMaxLen {
resp.IsCMD = false
break
}
m.Content = content
} else {
content := []rune(tmpContent[match.End():])
if len(content) > pattern.ContentMaxLen {
resp.IsCMD = false
break
}
m.Content = content
}
resp.Matches = append(resp.Matches, m)
end:
match = tmpNext
}
return resp
}