mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-08-17 19:47:10 +08:00
344 lines
9.5 KiB
Go
344 lines
9.5 KiB
Go
// messages.go 解析消息声明、字段数组和字段类型信息。
|
|
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// extractMessages 从多种 bundle 语法中提取消息声明。
|
|
func extractMessages(text string, moduleStarts []int) []Message {
|
|
var messages []Message
|
|
messageExists := func(typeName, varName string) bool {
|
|
for _, existing := range messages {
|
|
if existing.TypeName == typeName && existing.VarName == varName {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// 形式一:变量引用继承基类并在类体中声明 typeName 和 fields。
|
|
// 先找所有 "变量名 = class 内部类名" 定义
|
|
// JS 变量名可以包含 $ 符号,如 B$e, qg 等
|
|
// 需要同时捕获外部变量名和内部类名,因为字段引用可能用任一个
|
|
classDefRe := regexp.MustCompile(`([\w$]+)\s*=\s*class\s+([\w$]+)\s+extends\s+[\w$.]+\s*\{`)
|
|
classMatches := classDefRe.FindAllStringSubmatchIndex(text, -1)
|
|
|
|
// 从任意包的 this.typeName 字段读取完整类型名。
|
|
typeNameRe := regexp.MustCompile(`this\.typeName\s*=\s*"([\w.]+)"`)
|
|
|
|
// 从 this.fields 的 newFieldList 回调读取字段数组。
|
|
fieldsRe := regexp.MustCompile(`this\.fields\s*=\s*\w+(?:\.proto3)?\.util\.newFieldList\s*\(\s*\(\s*\)\s*=>\s*\[`)
|
|
|
|
for _, classMatch := range classMatches {
|
|
varName := text[classMatch[2]:classMatch[3]]
|
|
internalName := text[classMatch[4]:classMatch[5]]
|
|
classStart := classMatch[0]
|
|
|
|
// 找到类的结束位置(匹配大括号)
|
|
classEnd := findClassEnd(text, classMatch[1]-1)
|
|
if classEnd == -1 {
|
|
continue
|
|
}
|
|
|
|
classBody := text[classStart:classEnd]
|
|
|
|
// 在类体内查找 typeName
|
|
typeMatch := typeNameRe.FindStringSubmatch(classBody)
|
|
if typeMatch == nil {
|
|
continue
|
|
}
|
|
typeName := typeMatch[1]
|
|
|
|
// 在类体内查找 fields
|
|
fieldsMatch := fieldsRe.FindStringIndex(classBody)
|
|
if fieldsMatch == nil {
|
|
continue
|
|
}
|
|
|
|
// 找到 fields 数组的开始位置
|
|
bracketPos := classStart + fieldsMatch[1] - 1
|
|
fields := extractFieldArray(text, bracketPos)
|
|
|
|
pkg, shortName := parseTypeName(typeName)
|
|
msg := Message{
|
|
TypeName: typeName,
|
|
VarName: varName,
|
|
InternalName: internalName,
|
|
Fields: fields,
|
|
Package: pkg,
|
|
ShortName: shortName,
|
|
Pos: classStart,
|
|
ModuleStart: moduleStartForPos(moduleStarts, classStart),
|
|
}
|
|
messages = append(messages, msg)
|
|
}
|
|
|
|
// 形式二:匹配转译或压缩 bundle 中连续赋值的消息声明。
|
|
// 例如 i.runtime=n.proto3,i.typeName="agent.v1.McpArgs",i.fields=n.proto3.util.newFieldList(()=>[{...}])。
|
|
assignmentRe := regexp.MustCompile(`([\w$]+)\.typeName\s*=\s*"([\w.]+)"\s*,\s*[\w$]+\.fields\s*=\s*\w+(?:\.\w+)*\.util\.newFieldList\s*\(\s*\(\s*\)\s*=>\s*\[`)
|
|
assignmentMatches := assignmentRe.FindAllStringSubmatchIndex(text, -1)
|
|
for _, m := range assignmentMatches {
|
|
varName := text[m[2]:m[3]]
|
|
typeName := text[m[4]:m[5]]
|
|
|
|
// 跳过已经由类体形式提取的重复消息。
|
|
if messageExists(typeName, varName) {
|
|
continue
|
|
}
|
|
|
|
// 正则停在左方括号之前,从匹配尾部定位数组起点。
|
|
start := m[1] - 1
|
|
if start < 0 || start >= len(text) || text[start] != '[' {
|
|
continue
|
|
}
|
|
fields := extractFieldArray(text, start)
|
|
|
|
pkg, shortName := parseTypeName(typeName)
|
|
messages = append(messages, Message{
|
|
TypeName: typeName,
|
|
VarName: varName,
|
|
InternalName: "",
|
|
Fields: fields,
|
|
Package: pkg,
|
|
ShortName: shortName,
|
|
Pos: m[0],
|
|
ModuleStart: moduleStartForPos(moduleStarts, m[0]),
|
|
})
|
|
}
|
|
|
|
// 形式三:匹配现代 @bufbuild/protobuf 工厂调用。
|
|
// 例如 Req=A.makeMessageType("aiserver.v1.HasSeenAdRequest",()=>[{...}])。
|
|
messageFactoryRe := regexp.MustCompile(`([\w$]+)\s*=\s*[\w$.]+\.makeMessageType\s*\(\s*["']([\w.]+)["']\s*,\s*\(\s*\)\s*=>\s*\[`)
|
|
factoryMatches := messageFactoryRe.FindAllStringSubmatchIndex(text, -1)
|
|
for _, m := range factoryMatches {
|
|
varName := text[m[2]:m[3]]
|
|
typeName := text[m[4]:m[5]]
|
|
if messageExists(typeName, varName) {
|
|
continue
|
|
}
|
|
|
|
bracketStart := m[1] - 1
|
|
if bracketStart < 0 || bracketStart >= len(text) || text[bracketStart] != '[' {
|
|
continue
|
|
}
|
|
|
|
pkg, shortName := parseTypeName(typeName)
|
|
messages = append(messages, Message{
|
|
TypeName: typeName,
|
|
VarName: varName,
|
|
Fields: extractFieldArray(text, bracketStart),
|
|
Package: pkg,
|
|
ShortName: shortName,
|
|
Pos: m[0],
|
|
ModuleStart: moduleStartForPos(moduleStarts, m[0]),
|
|
})
|
|
}
|
|
|
|
// 空消息直接传字段数组,不使用延迟回调。
|
|
// 例如 Res=A.makeMessageType("aiserver.v1.MarkAdAsSeenResponse",[])。
|
|
emptyMessageFactoryRe := regexp.MustCompile(`([\w$]+)\s*=\s*[\w$.]+\.makeMessageType\s*\(\s*["']([\w.]+)["']\s*,\s*\[`)
|
|
emptyFactoryMatches := emptyMessageFactoryRe.FindAllStringSubmatchIndex(text, -1)
|
|
for _, m := range emptyFactoryMatches {
|
|
varName := text[m[2]:m[3]]
|
|
typeName := text[m[4]:m[5]]
|
|
if messageExists(typeName, varName) {
|
|
continue
|
|
}
|
|
|
|
bracketStart := m[1] - 1
|
|
if bracketStart < 0 || bracketStart >= len(text) || text[bracketStart] != '[' {
|
|
continue
|
|
}
|
|
|
|
pkg, shortName := parseTypeName(typeName)
|
|
messages = append(messages, Message{
|
|
TypeName: typeName,
|
|
VarName: varName,
|
|
Fields: extractFieldArray(text, bracketStart),
|
|
Package: pkg,
|
|
ShortName: shortName,
|
|
Pos: m[0],
|
|
ModuleStart: moduleStartForPos(moduleStarts, m[0]),
|
|
})
|
|
}
|
|
|
|
return messages
|
|
}
|
|
|
|
// findClassEnd 查找类定义的配对右花括号。
|
|
func findClassEnd(text string, openBrace int) int {
|
|
depth := 0
|
|
for i := openBrace; i < len(text); i++ {
|
|
if text[i] == '{' {
|
|
depth++
|
|
} else if text[i] == '}' {
|
|
depth--
|
|
if depth == 0 {
|
|
return i + 1
|
|
}
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
// extractFieldArray 从左方括号位置解析完整字段数组。
|
|
func extractFieldArray(text string, start int) []Field {
|
|
// 查找字段数组的配对右方括号。
|
|
depth := 0
|
|
end := start
|
|
for i := start; i < len(text); i++ {
|
|
if text[i] == '[' {
|
|
depth++
|
|
} else if text[i] == ']' {
|
|
depth--
|
|
if depth == 0 {
|
|
end = i + 1
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
arrayText := text[start:end]
|
|
|
|
// 按每个花括号块解析独立字段对象。
|
|
var fields []Field
|
|
|
|
// 依次查找字段对象。
|
|
fieldObjects := extractFieldObjects(arrayText)
|
|
|
|
for _, fieldObj := range fieldObjects {
|
|
field, parseErr := parseFieldObject(fieldObj)
|
|
if parseErr != nil {
|
|
activeDiagnostics.addSkippedField(fieldObj, parseErr)
|
|
continue
|
|
}
|
|
activeDiagnostics.addParsedField()
|
|
fields = append(fields, *field)
|
|
}
|
|
|
|
return fields
|
|
}
|
|
|
|
// extractFieldObjects 从数组文本中提取独立字段对象。
|
|
func extractFieldObjects(arrayText string) []string {
|
|
var objects []string
|
|
depth := 0
|
|
start := -1
|
|
|
|
for i := 0; i < len(arrayText); i++ {
|
|
if arrayText[i] == '{' {
|
|
if depth == 0 {
|
|
start = i
|
|
}
|
|
depth++
|
|
} else if arrayText[i] == '}' {
|
|
depth--
|
|
if depth == 0 && start >= 0 {
|
|
objects = append(objects, arrayText[start:i+1])
|
|
start = -1
|
|
}
|
|
}
|
|
}
|
|
|
|
return objects
|
|
}
|
|
|
|
// parseFieldObject 解析包含编号、名称、类型和修饰符的单个字段对象。
|
|
func parseFieldObject(obj string) (*Field, error) {
|
|
// 提取字段编号。
|
|
noMatch := noRe.FindStringSubmatch(obj)
|
|
if noMatch == nil {
|
|
return nil, errors.New("missing field no")
|
|
}
|
|
no, _ := strconv.Atoi(noMatch[1])
|
|
|
|
// 提取字段名称。
|
|
nameMatch := nameRe.FindStringSubmatch(obj)
|
|
if nameMatch == nil {
|
|
return nil, errors.New("missing field name")
|
|
}
|
|
name := strings.TrimSpace(nameMatch[1])
|
|
if !fieldNameRe.MatchString(name) {
|
|
return nil, fmt.Errorf("invalid field name: %s", name)
|
|
}
|
|
|
|
// 提取字段类别。
|
|
kindMatch := kindRe.FindStringSubmatch(obj)
|
|
if kindMatch == nil {
|
|
return nil, errors.New("missing field kind")
|
|
}
|
|
kind := strings.TrimSpace(kindMatch[1])
|
|
|
|
field := &Field{
|
|
No: no,
|
|
Name: name,
|
|
Kind: kind,
|
|
}
|
|
|
|
// 类型 T 可以是标量编号、变量名或 getEnumType 枚举调用。
|
|
|
|
// 枚举优先匹配 getEnumType 调用。
|
|
if enumMatch := enumTypeRe.FindStringSubmatch(obj); enumMatch != nil {
|
|
field.T = enumMatch[1]
|
|
} else {
|
|
// 其余类型匹配普通 T 属性值。
|
|
if tMatch := tRe.FindStringSubmatch(obj); tMatch != nil {
|
|
if t, err := strconv.Atoi(tMatch[1]); err == nil {
|
|
field.T = t
|
|
} else {
|
|
field.T = tMatch[1]
|
|
}
|
|
} else if shorthandTRe.MatchString(obj) {
|
|
field.T = "T"
|
|
}
|
|
}
|
|
|
|
// 仅在当前字段对象内检查 oneof 分组。
|
|
if oneofMatch := oneofRe.FindStringSubmatch(obj); oneofMatch != nil {
|
|
candidate := strings.TrimSpace(oneofMatch[1])
|
|
if oneofNameRe.MatchString(candidate) {
|
|
field.Oneof = candidate
|
|
}
|
|
}
|
|
|
|
// 仅在当前字段对象内检查 repeated;压缩 JS 中 !0 表示真。
|
|
if repeatedRe.MatchString(obj) {
|
|
field.Repeated = true
|
|
}
|
|
|
|
// 仅在当前字段对象内检查 optional。
|
|
if optRe.MatchString(obj) {
|
|
field.Opt = true
|
|
}
|
|
|
|
// map 字段通过 K 键类型和 V 值描述共同表示。
|
|
if field.Kind == "map" {
|
|
// 提取 map 键类型。
|
|
if keyMatch := keyRe.FindStringSubmatch(obj); keyMatch != nil {
|
|
field.MapKey, _ = strconv.Atoi(keyMatch[1])
|
|
}
|
|
|
|
// 提取 map 值类型,兼容属性顺序变化。
|
|
if valueMatch := mapValueRe.FindStringSubmatch(obj); valueMatch != nil {
|
|
valueObj := valueMatch[1]
|
|
if kindMatch := mapValueKRe.FindStringSubmatch(valueObj); kindMatch != nil {
|
|
field.MapValueKind = kindMatch[1]
|
|
}
|
|
if tMatch := mapValueTRe.FindStringSubmatch(valueObj); tMatch != nil {
|
|
if t, err := strconv.Atoi(tMatch[1]); err == nil {
|
|
field.MapValueT = t
|
|
} else {
|
|
field.MapValueT = tMatch[1]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return field, nil
|
|
}
|