Сопоставление с образцом для AST Go

Вопрос или проблема

Я пытаюсь извлечь некоторую информацию из 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 не предоставляется готовый инструмент для паттерн-матчинга, вы можете улучшить код, интегрировав подходы с использованием вспомогательных функций или направленного поиска. Вот несколько рекомендаций, которые могут помочь:

  1. Декомпозиция кода: Вместо того чтобы иметь один большой метод, который выполняет все проверки, разбейте логику на несколько вспомогательных функций. Это упростит тестирование и чтение вашего кода.

  2. Использование рекурсии: Можно создать рекурсивный метод, который будет обходить узлы AST и проверять их на соответствие искомому паттерну, что сократит дублирование кода.

  3. Определение паттернов: Создайте структуры данных для хранения паттернов, которые вам нужны. Это позволит вам упростить процесс проверки и сделать его более декларативным.

Пример улучшенного подхода

В следующем примере демонстрируется, как следует улучшить ваш код, разбив его на функции и добавив возможность рекурсивного обхода:

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.

Оцените материал
Добавить комментарий

Капча загружается...