使用RegexParser进行Scala read-eval-print而没有那么多样板文件?

时间:2011-12-02 16:41:39

标签: parsing scala

我正在实现Scala程序的一部分,该程序接受"functionName arg1=x1 arg2=x2 ..."形式的输入字符串,将xi解析为正确的类型,然后调用相应的Scala函数functionName(x1,x2,...)。下面的代码是一个带有两个函数foobar的示例实现,它们使用不同类型的参数。

请注意,foobar的类型和参数名称必须在几个地方手写到代码中:原始函数定义,定义解析器返回的case类,以及解析器他们自己。解析器返回的case类基本上没什么兴趣 - 我很想从解析器中调用foo和bar,但我觉得这样会很icky。

我的问题是:这个实现可以简化吗?在实践中,我将有许多具有复杂参数类型的函数,我宁愿能够尽可能少地指定这些类型,也许还不必定义相应的case类。

type Word = String
// the original function definitions
def foo(x: Int, w: Word) = println("foo called with " + x + " and " + w)
def bar(y: Int, z: Int)  = println("bar called with " + y + " and " + z)

// the return type for the parser
abstract class Functions
case class Foo(x: Int, w: Word) extends Functions
case class Bar(y: Int, z: Int) extends Functions

object FunctionParse extends RegexParsers {
  val int = """-?\d+""".r ^^ (_.toInt)
  val word = """[a-zA-Z]\w*""".r

  val foo = "foo" ~> ("x=" ~> int) ~ ("w=" ~> word) ^^ { case x~w => Foo(x,w) }
  val bar = "bar" ~> ("y=" ~> int) ~ ("z=" ~> int) ^^ { case y~z => Bar(y,z) }
  val function = foo | bar

  def parseString(s: String) = parse(function, s)
}


def main(args: Array[String]) = {
  FunctionParse.parseString(args.mkString(" ")) match {
    case FunctionParse.Success(result, _) => result match {
      case Foo(x, w) => foo(x, w)
      case Bar(y, z) => bar(y, z)
    }
    case _ => println("sux.")
  }
}

编辑:我应该注意,在我的情况下,输入字符串的上述特定格式不是很重要 - 如果它导致更简洁,更简单的Scala代码,我很乐意改变它(使用xml或其他)。

2 个答案:

答案 0 :(得分:3)

你想要反思,简单地说。反射意味着在运行时而不是编译时找出,实例化和调用类和方法。例如:

scala> val clazz = Class forName "Foo"
clazz: Class[_] = class Foo

scala> val constructors = clazz.getConstructors
constructors: Array[java.lang.reflect.Constructor[_]] = Array(public Foo(int,java.lang.String))

scala> val constructor = constructors(0)
constructor: java.lang.reflect.Constructor[_] = public Foo(int,java.lang.String)

scala> constructor.getParameter
getParameterAnnotations   getParameterTypes         

scala> val parameterTypes = constructor.getParameterTypes
parameterTypes: Array[Class[_]] = Array(int, class java.lang.String)

scala> constructor.newInstance(5: Integer, "abc")
res6: Any = Foo(5,abc)

这是所有Java反射。 Scala 2.9仍然没有Scala特定的反射界面,虽然其中一个已经在开发中,并且可能在下一版本的Scala上可用。

答案 1 :(得分:0)

你在做什么看起来很合理。在我的脑海中“简化”它的唯一方法是使用不太明确的类型和/或使用反射来查找适当的函数...

更新: Daniel的回答是如何使用反射的一个很好的例子。就不太明确的类型而言,您必须将函数参数设为Any ...