有效实现通用匹配令牌

时间:2013-11-11 17:43:36

标签: parsing scala case-class traits

这是我在我的小解析器应用程序中使用的基本特征:

trait Token[ValueType] {
    def value: ValueType
}

这就是我的要求。我还想结合Java的一个很好的特性和Scala的一个很好的特性,即:

  • 用于枚举的Java方法(它们是使用方法的普通对象,可以是ihnerit等。)
  • Scala支持匹配(可读代码)

所以扩展Token的类的示例是:

// this to emulate Java enums; ProperEnumeration just adds some simple methods like fromChars etc.
object Keywords extends ProperEnumeration {
    val AND, ARRAY, BEGIN, CASE, CONST, ... = Value
}

// this to enable matching
final case class Keyword(keyword: Keywords.Value) extends Token[Keywords.Value] {
    def this(string: String) = this(Keywords.fromString(string))   
    def value = keyword
}

object SpecialSymbols extends ProperEnumeration {
    val LEFT_BRACE = Value("{")
    val RIGHT_BRACE = Value("}")
    ...
}

final case class SpecialSymbol(symbol: SpecialSymbols.Value) extends Token[SpecialSymbols.Value]     {
    def this(symbol: String) = this(SpecialSymbols.fromString(symbol))
    def value = symbol
}

// there are also non-enum subclasses of Token
case class Identifier(identifier: String) extends Token[String] {
    override def value: String = identifier
}

这是我想出的最好的。我可以这样使用它:

token match {
    case Keyword(Keywords.BEGIN) => ...
    case SpecialSymbol(SpecialSymbols.LEFT_BRACE) => ...
    case Identifier(name) => ...
}

我想修改它以使我更简洁,我想要这样的事情:

token match {
        case Keyword.BEGIN => ... // or Keyword(BEGIN)
        case SpecialSymbol.LEFT_BRACE => ...
        case Identifier(name) => ...
    }

还支持一个名为consume的方法,该方法适用于任何类型的Token子类(consume如果源中的下一个标记不是提供的参数,则应该抛出异常)。

consume(Keyword.BEGIN);
consume(SpecialSymbol.LEFT_BRACE);
consume(Identifier(name));

我希望代码干净,这就是我首先使用Scala的原因。所以我希望没有函数重载,以便轻松添加Trait子类。

所以,亲爱的Scalists,该怎么办?

2 个答案:

答案 0 :(得分:1)

最简单的方法

case Keyword(BEGIN) => ...

只是import关键字:

import Keywords._

它不会要求对您的代码进行任何其他更改。

答案 1 :(得分:1)

但是,我个人更愿意避免ProperEnumerarion并且具有简单的特征和案例对象层次结构:

trait Keyword
case object BEGIN extends Keyword
case object CASE extends Keyword

这将自动提供模式匹配:

token match {
  case BEGIN => ...
}

如果你需要对象中的某些方法,你可能会在具有一些抽象类(带有构造函数)的特性中声明它们:

abstract class Keyword(val name:String) {
  def myMethod = "Keyword."+name
}
case object BEGIN extends Keyword("BEGIN")
case object CASE extends Keyword("CASE")

UPD:您可以使用与枚举实例化“枚举”实例的对象:

object AllMyKeywords {
  val BEGIN = Keyword("BEGIN")
  val CASE = Keyword("CASE")
  // etc.
  val values = List(BEGIN, CASE, ...).map(k => (k.name, k)).toMap
}

UPD2:还有一种方法可以与字符串进行模式匹配:

abstract class Keyword(val name:String) {
  def unapply(str:String):Option[Keyword] = {
    if(AllMyKeywords.values.contains(str))
      Some(AllMyKeywords.values(str)
    else
      None
  }
}

在这种情况下,unapply方法由每个BEGINCASE关键字实现,因此直接调用(至少我认为是这样)。

"BEGIN" match { case BEGIN => ??? }