Вопрос или проблема
Я пытаюсь извлечь некоторую информацию из AST (абстрактного синтаксического дерева) Go. Даже очень простые задачи оказываются довольно громоздкими:
//
// Поиск верхнеуровневого объявления "var passwd = []*PwEntry{...}".
//
func (this *Source) FindPasswdDecl() *ast.CompositeLit {
for _, node := range this.AstFile.Decls {
switch decl := node.(type) {
case *ast.GenDecl:
if decl.Tok == token.VAR {
// fmt.Println("найдено: var")
for _, spec := range decl.Specs {
switch vspec := spec.(type) {
case *ast.ValueSpec:
if len(vspec.Names) == 1 && vspec.Names[0].Name == "passwd" {
// fmt.Println("найдено: passwd")
if len(vspec.Values) == 1 {
switch cl := vspec.Values[0].(type) {
case *ast.CompositeLit:
switch clt := cl.Type.(type) {
case *ast.ArrayType:
// fmt.Println("найдено: []")
switch se := clt.Elt.(type) {
case *ast.StarExpr:
// fmt.Println("найдено: *")
switch sx := se.X.(type) {
case *ast.Ident:
if sx.Name == "PwEntry" {
// fmt.Println("найдено: PwEntry")
return cl
}
}
}
}
}
}
}
}
}
}
}
}
return nil
}
Кто-нибудь знает шаблонизатор для AST Go?
Ответ или решение
Паттерн-матчинг для AST Go
При работе с AST (Abstract Syntax Tree) в языке программирования Go часто возникает необходимость извлечь определенную информацию из кода, что может стать довольно громоздким и фрагментированным процессом, как это показано в вашем примере. Вы выразили интерес к более элегантному способу паттерн-матчинга для Go AST.
Проблема
Ваш код, который ищет определение переменной var passwd = []*PwEntry{...}
, представляет собой последовательность вложенных операторов switch
, каждый из которых пытается проверить соответствие конкретному типу узла AST. Это может быстро стать запутанным и трудным для понимания, особенно если логика поиска усложняется.
Решение: Использование Паттерн-Матчинга
Хотя в стандартной библиотеке Go не предоставляется готовый инструмент для паттерн-матчинга, вы можете улучшить код, интегрировав подходы с использованием вспомогательных функций или направленного поиска. Вот несколько рекомендаций, которые могут помочь:
-
Декомпозиция кода: Вместо того чтобы иметь один большой метод, который выполняет все проверки, разбейте логику на несколько вспомогательных функций. Это упростит тестирование и чтение вашего кода.
-
Использование рекурсии: Можно создать рекурсивный метод, который будет обходить узлы AST и проверять их на соответствие искомому паттерну, что сократит дублирование кода.
-
Определение паттернов: Создайте структуры данных для хранения паттернов, которые вам нужны. Это позволит вам упростить процесс проверки и сделать его более декларативным.
Пример улучшенного подхода
В следующем примере демонстрируется, как следует улучшить ваш код, разбив его на функции и добавив возможность рекурсивного обхода:
func (s *Source) FindPasswdDecl() *ast.CompositeLit {
return s.findPasswdInDecls(s.AstFile.Decls)
}
func (s *Source) findPasswdInDecls(decls []ast.Decl) *ast.CompositeLit {
for _, decl := range decls {
if genDecl, ok := decl.(*ast.GenDecl); ok && genDecl.Tok == token.VAR {
if result := s.findPasswdInSpecs(genDecl.Specs); result != nil {
return result
}
}
}
return nil
}
func (s *Source) findPasswdInSpecs(specs []ast.Spec) *ast.CompositeLit {
for _, spec := range specs {
if valueSpec, ok := spec.(*ast.ValueSpec); ok && len(valueSpec.Names) == 1 && valueSpec.Names[0].Name == "passwd" {
return s.extractCompositeLit(valueSpec.Values)
}
}
return nil
}
func (s *Source) extractCompositeLit(values []ast.Expr) *ast.CompositeLit {
if len(values) == 1 {
if cl, ok := values[0].(*ast.CompositeLit); ok && s.isValidCompositeLit(cl) {
return cl
}
}
return nil
}
func (s *Source) isValidCompositeLit(cl *ast.CompositeLit) bool {
if arrType, ok := cl.Type.(*ast.ArrayType); ok {
if starExpr, ok := arrType.Elt.(*ast.StarExpr); ok {
if ident, ok := starExpr.X.(*ast.Ident); ok && ident.Name == "PwEntry" {
return true
}
}
}
return false
}
Выводы
Хотя ваш оригинальный код решает задачу перед вами, он может быть сложным для сопровождения и понимания. Попробуйте интегрировать предложенные подходы, чтобы создать более элегантное и эффективное решение для паттерн-матчинга в AST Go. Создание вспомогательных функций и применение рекурсивного обхода может значительно улучшить читабельность вашего кода и упростить внесение изменений в будущем.
Если вам нужна помощь с конкретными задачами или дополнительными вопросами по Go AST, не стесняйтесь обращаться к сообществу или искать дополнительные ресурсы.
SEO Оптимизация
- Паттерн-матчинг для Go AST
- Эффективная работа с AST в Go
- Примеры кода Go для обработки AST
- Оптимизация кода на Go
- Как извлекать данные из Go AST
Эти ключевые фразы помогут пользователям находить вашу статью в поисковых системах по запросам, связанным с работой с AST в Go.