Scala:如何处理类型集合?

时间:2016-01-24 20:12:52

标签: scala

我正在手工编写一个小型语言的递归下降解析器。在我的词霸中,我有:

trait Token{def position:Int}
trait Keyword  extends Token
trait Operator extends Token

case class Identifier(position:Int, txt:String) extends Token
case class If        (position:Int)             extends Keyword
case class Plus      (position:Int)             extends Operator
/* etcetera; one case class per token type */

我的解析器效果很好,现在我想加入一些错误恢复:替换,插入或丢弃令牌,直到某个同步点。

为此,有一个函数,在无效的Scala中看起来像这样的

是很方便的。
def scanFor(tokenSet:Set[TokenClass], lookahead:Int) = {
  lexer.upcomingTokens.take(lookahead).find{ token =>
    tokenSet.exists(tokenClass => token.isInstanceOf[tokenClass])
  }
}

我会打电话,例如:scanFor(Set(Plus, Minus, Times, DividedBy), 4)

但是TokenClass当然不是有效类型,我不知道如何创建上一组。

作为替代方案:

  1. 我可以创建一个新特征并在令牌集中创建所有令牌类,我想检查以扩展该特征,然后只针对该特征进行instanceOf检查。但是,我可能有几个这样的集合,这些集合很难命名,而且代码很难在以后维护。
  2. 我可以创建isXXX:Token =>布尔函数,并制作那些套装,但它似乎不太优雅
  3. 有什么建议吗?

1 个答案:

答案 0 :(得分:3)

我实际上建议,如果只有少数这样的组合,使用额外的特性。它易于编写和理解,并且在运行时会很快。说起来并不是那么糟糕

case class Plus(position: Int)
extends Operator with Arithmetic with Precedence7 with Unary

但是有很多种选择。

如果你不介意一个挑剔的手动维护过程并且需要非常快的东西,那么为每个标记类型定义一个ID号(必须手动保持不同)将允许你使用Set[Int]或{{1或者甚至只是BitSet来选择你喜欢的那些类。然后,您可以设置操作(并集,交集)以相互构建这些选择器。编写单元测试以帮助使得挑剔的位更加可靠并不难。如果您至少可以设法列出所有类型:

Long

因此,如果您认为性能和可组合性至关重要,那么您不应该对此方法过于担心。

你也可以编写自定义提取器,它可以(更慢)通过模式匹配拉出正确的标记子集。例如,

val everyone = Seq(Plus, Times, If /* etc */)
assert(everyone.length == everyone.map(_.id).toSet.size)
如果不是正确的操作类型,

会给你object ArithOp { def unapply(t: Token): Option[Operator] = t match { case o: Operator => o match { case _: Plus | _: Minus | _: Times | _: DividedBy => Some(o) case _ => None } case _ => None } } 。 (在这种情况下,我假设除了None之外没有父母。)

最后,您可以将您的类型表达为工会和HLists,然后使用Shapeless以这种方式选择它们,但我没有亲身使用解析器的经验,所以我不确定您可能遇到的困难