获取有关 Groovy 函数的信息(名称、签名、主体代码)

2024-06-22

我有一个 Groovy 文件,其中包含一堆简单的函数,如下所示:

// useful functions
def myFunc1(String arg) {
    println("Hello " + arg)
}

def myFunc2(String arg) {
    println("Goodbye " + arg)
}

我想从中获得:

  • 方法名称
  • 论据
  • 函数体代码

(全部都是简单的字符串,我还不需要运行任何东西。)

我本来打算求助于一些正则表达式,但由于我使用的是 JVM 语言(Scala),我想我也许可以使用一些 Groovy 编译器的东西来以“更好”的方式做到这一点。

似乎有相当多的关于动态加载 Groovy 代码并运行它的信息,但关于内省源代码的信息却很少。有任何想法吗?

(如果没有“好”的方式,我也会接受一些 Scala-foo 来以简洁的方式解析信息。)


这是可行的,并演示了在 AST 中查找每个重要节点所需的令牌类型。希望它有意义...通过使用大量的 Groovy 活力,我希望我没有让移植到 Scala 变得太困难:-(

import org.codehaus.groovy.antlr.*
import org.codehaus.groovy.antlr.parser.*
import static org.codehaus.groovy.antlr.parser.GroovyTokenTypes.*

def code = '''
// useful functions
def myFunc1(String arg) {
    println("Hello " + arg)
}

def myFunc2(arg, int arg2) {
    println("Goodbye " + arg)
}

public String stringify( int a ) {
  "$a"
}
'''

def lines = code.split( '\n' )

// Generate a GroovyRecognizer, compile an AST and assign it to 'ast'
def ast = new SourceBuffer().with { buff ->
  new UnicodeEscapingReader( new StringReader( code ), buff ).with { read ->
    read.lexer = new GroovyLexer( read )
    GroovyRecognizer.make( read.lexer ).with { parser ->
      parser.sourceBuffer = buff
      parser.compilationUnit()
      parser.AST
    }
  }
}

// Walks the ast looking for types
def findByPath( ast, types, multiple=false ) {
  [types.take( 1 )[ 0 ],types.drop(1)].with { head, tail ->
    if( tail ) {
      findByPath( ast*.childrenOfType( head ).flatten(), tail, multiple )
    }
    else {
      ast*.childrenOfType( head ).with { ret ->
        multiple ? ret[ 0 ] : ret.head()[0]
      }
    }
  }
}

// Walk through the returned ast
while( ast ) {
  def methodModifier = findByPath( ast, [ MODIFIERS   ] ).firstChild?.toStringTree() ?: 'public'
  def returnType     = findByPath( ast, [ TYPE, IDENT ] ) ?: 'Object'
  def methodName     = findByPath( ast, [ IDENT       ] )
  def body           = findByPath( ast, [ SLIST ] )
  def parameters     = findByPath( ast, [ PARAMETERS, PARAMETER_DEF ], true ).collect { param ->
    [ type: findByPath( param, [ TYPE ] ).firstChild?.toStringTree() ?: 'Object',
      name: findByPath( param, [ IDENT ] ) ]
  }

  def (y1,y2,x1,x2) = [ body.line - 1, body.lineLast - 1, body.column - 1, body.columnLast ]
  // Grab the text from the original string
  def snip = [  lines[ y1 ].drop( x1 ),                // First line prefix stripped
               *lines[ (y1+1)..<y2 ],                  // Mid lines
                lines[ y2 ].take( x2 ) ].join( '\n' )  // End line suffix stripped

  println '------------------------------'
  println "modifier: $methodModifier"
  println "returns:  $returnType"
  println "name:     $methodName"
  println "params:   $parameters"
  println "$snip\n"

  // Step to next branch and repeat
  ast = ast.nextSibling
}

它打印出:

------------------------------
modifier: public
returns:  Object
name:     myFunc1
params:   [[type:String, name:arg]]
{
    println("Hello " + arg)
}

------------------------------
modifier: public
returns:  Object
name:     myFunc2
params:   [[type:Object, name:arg], [type:int, name:arg2]]
{
    println("Goodbye " + arg)
}

------------------------------
modifier: public
returns:  String
name:     stringify
params:   [[type:int, name:a]]
{
  "$a"
}

希望它有帮助,或者为您指明正确的方向:-)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

获取有关 Groovy 函数的信息(名称、签名、主体代码) 的相关文章

随机推荐