如何匹配Clojure中的多行正则表达式来解析Groovy源文件?

时间:2012-05-09 10:25:29

标签: java regex groovy clojure

我正在尝试在Groovy源文件上运行Clojure正则表达式来解析各个函数。

// gremlin.groovy

def warm_cache() {
  for (vertex in g.getVertices()) {
    vertex.getOutEdges()
  }
}

def clear() {
  g.clear()
}

这是我在Clojure中使用的模式:

(def source (read-file "gremlin.groovy"))

(def pattern #"(?m)^def.*[^}]")   

(re-seq pattern source)

然而,它只抓住了第一行,而不是多行功能。

3 个答案:

答案 0 :(得分:6)

作为演示如何从GroovyRecognizer获取AST并避免尝试使用正则表达式解析语言的演示,您可以在Groovy中执行此操作:

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

def code = '''
// gremlin.groovy

def warm_cache() {
  for (vertex in g.getVertices()) {
    vertex.getOutEdges()
  }
}

def clear() {
  g.clear()
}
'''


def ast = new GroovyRecognizer( new GroovyLexer( new StringReader( code ) ).plumb() ).with { p ->
  p.compilationUnit()
  p.AST
}


while( ast ) {
  println ast.toStringTree()
  ast = ast.nextSibling
}

打印出AST中每个GroovySourceAST节点的AST,给你(对于这个例子):

 ( METHOD_DEF MODIFIERS TYPE warm_cache PARAMETERS ( { ( for ( in vertex ( ( ( . g getVertices ) ELIST ) ) ( { ( EXPR ( ( ( . vertex getOutEdges ) ELIST ) ) ) ) ) )
 ( METHOD_DEF MODIFIERS TYPE clear PARAMETERS ( { ( EXPR ( ( ( . g clear ) ELIST ) ) ) )

你应该可以用Clojure的java interop和groovy-all jar文件做同样的事情


修改

要获得更多信息,您只需要深入了解AST并稍微操作输入脚本。将上述代码中的while循环更改为:

while( ast ) {
  if( ast.type == GroovyTokenTypes.METHOD_DEF ) {
    println """Lines $ast.line to $ast.lineLast
              |  Name:  $ast.firstChild.nextSibling.nextSibling.text
              |  Code:  ${code.split('\n')[ (ast.line-1)..<ast.lineLast ]*.trim().join( ' ' )}
              |   AST:  ${ast.toStringTree()}""".stripMargin()
  }
  ast = ast.nextSibling
}

打印出来:

Lines 4 to 8
  Name:  warm_cache
  Code:  def warm_cache() { for (vertex in g.getVertices()) { vertex.getOutEdges() } }
   AST:   ( METHOD_DEF MODIFIERS TYPE warm_cache PARAMETERS ( { ( for ( in vertex ( ( ( . g getVertices ) ELIST ) ) ( { ( EXPR ( ( ( . vertex getOutEdges ) ELIST ) ) ) ) ) )
Lines 10 to 12
  Name:  clear
  Code:  def clear() { g.clear() }
   AST:   ( METHOD_DEF MODIFIERS TYPE clear PARAMETERS ( { ( EXPR ( ( ( . g clear ) ELIST ) ) ) )

显然,Code:部分只是连接在一起的线条,所以如果粘贴回groovy可能不起作用,但是它们会让你知道原始代码......

答案 1 :(得分:3)

这是你的正则表达,而不是Clojure。您请求匹配def,然后匹配任何内容,然后匹配一个不等于右括号的char。这个char可以在任何地方。你想要实现的目标是:(?sm)def.*?^}

答案 2 :(得分:2)

简短回答

(re-seq (Pattern/compile "(?m)^def.*[^}]" Pattern/MULTILINE) source)

来自http://docs.oracle.com/javase/1.4.2/docs/api/java/util/regex/Pattern.html

  

默认情况下,正则表达式^和$忽略行终止符,并且仅分别匹配整个输入序列的开头和结尾。如果激活MULTILINE模式,则^在输入开始时和任何行终止符之后匹配,但输入结束时除外。当处于MULTILINE模式时,$匹配在行终止符之前或输入序列的结尾。

您需要能够传入

Pattern.MULTILINE

编译模式时。但是在re-seq上没有这个选项,所以你可能需要进入Java互操作才能使它正常工作?理想情况下,你真的应该能够在Clojure土地上指定它...... :(

更新: 实际上,它并不是那么糟糕。而不是使用正则表达式的文字表达式,只需使用Java interop作为您的模式。请改用(re-seq (Pattern/compile "(?m)^def.*[^}]" Pattern/MULTILINE) source)(假设您已导入java.util.regex.Pattern)。我没有对此进行过测试,但我认为这样可以帮到你。