我正在编写一个简单的解析器是Scala。

我有一个基本特征,代表文件中的一个元素。

trait Token[T] {
    def stringValue: String
    def value: T
}


这就是我需要的-字符串(文本)值和解析后的值(有时会是相同的字符串)。现在,我想拥有一组以下子类:


保留的符号/关键字,例如classvoid
特殊符号,例如+/
整数文字,例如123
真实文字,例如1.23
字符串文字,例如'123'


您将如何实现这样的层次结构?由于这是有限的,因此最好使用case类。但是,枚举也将很棒。.如何结合?



换句话说,用Scala式的方式在Scala中(下文)编写此内容的最佳方法是什么?

public interface Token<T> {
    String stringValue();
    T value();
}

public enum ReservedSymbol implements Token<ReservedSymbol> {
    CLASS('class'), VOID('void');

    private String val;
    private ReservedSymbol(String val) { this.val = val; }

    public String stringValue() { return val; }
    public ReservedSymbol value() { return this; }
}


public class IntegerLiteral implements Token<Integer> {
    private Integer val;
    public IntegerLiteral(String val) { this.val = Integer.valueOf(val); }

    public String stringValue() { return val.toString(); }
    public Integer value() { return val; }
}


等等

最佳答案

在Scala中构建此类层次结构时,请尝试应用以下原则:


绘制所需的类层次结构。以仅实例化叶节点而内部节点为抽象的方式进行设计。
将内部节点实现为特征
将叶子节点实现为案例类


这样做的原因是,案例类会自动添加很多有用的魔术(toString,取消应用,序列化,等于等)。但是必要的代码随即产生,与案例类之间的继承不兼容(例如equals would not work properly)。

通常,不带参数的叶类型通常建模为case object,而带参数的叶类型通常建模为case class

当您需要实例化类型树的内部节点时,只需添加一个人工叶子并将其实现为case类/ case对象。

您也可以在Scala中使用Enumerations,但是通常case类更实用。当需要将给定的字符串转换为相应的枚举时,枚举通常是一个不错的选择。您可以使用Enumeration.withName(String)执行此操作。

在Alexey Romanov的回答中,您可以看到如何将此原理应用于具有一个根节点和三个叶节点的类型树。


Token(内部节点=>特征)

1.1。 ClassSymbol(不带参数的叶节点=>案例对象)

1.2。 VoidSymbol(不带参数的叶节点=>案例对象)

1.3。 IntegerLiteral。 (具有参数的叶节点=>案例类)


使用枚举和案例类的情况的示例:

trait Token[T]{
  def stringValue: String
  def value: T
}

object ReservedSymbolEnum extends Enumeration {
  type ReservedSymbolEnum = Value
  val `class`, `void` = Value
      val NullValue = Value("null") // Alternative without quoting
}

case class ReservedSymbol(override val stringValue: String)extends Token[ReservedSymbolEnum.ReservedSymbolEnum] {
  def value = ReservedSymbolEnum.withName(stringValue)
}

case class StringLiteral(override val stringValue: String) extends Token[String] {
  override def value = stringValue
}

case class IntegerLitaral(override val stringValue: String) extends Token[Int] {
  override def value = stringValue.toInt
}


一些用法示例:

scala> def `void`=ReservedSymbol("void")
void: ReservedSymbol

scala> `void`.value
res1: ReservedSymbolEnum.Value = void

scala> def `42`=IntegerLiteral("42")
42: IntegerLitaral

scala> `42`.value
res2: Int = 42

07-28 02:33
查看更多