2013-09-16 3 views
2

Я пишу простой парсер Scala.Scala enum extend trait

У меня есть базовый признак, который представляет элемент в файле.

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

Это то, что мне нужно - строковое значение (текст) и разобранное значение (которое иногда выйдет той же строки). Теперь я хочу иметь набор подклассов для:

  • зарезервированные символы/ключевые слова, например. class, void и т.д.
  • специальные символы, например. +, / и т.д.
  • целые литералы, например. 123
  • настоящие литералы, например. 1.23
  • строковые литералы, например. '123'

Как реализовать такую ​​иерархию? Поскольку это конечно, было бы неплохо использовать класс case. Думаю. Но перечисление тоже было бы здорово. Как совместить?


Другими словами, что это лучший способ, чтобы написать это (ниже) в Scala в более Scal-МОГ способом?

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; } 
} 

т.д.

ответ

5

При построении такой иерархии в Scala, попробуйте применить следующие принципы:

  1. Sketch класс иерархии вам нужно. Создайте его так, чтобы создавались только узлы листа, а внутренние узлы были абстрактными.
  2. Реализовать внутренние узлы, как черты
  3. Реализовать узлы листа в случае классов

Причины этого в том, что классы случае добавляют много полезной магию автоматически (ToString, исключить его, сериализацию, равен, и т.д.). Но необходимый код генерируется удаленно, что несовместимо с наследованием между классами case (например, equals would not work properly).

Обычно типы листьев без параметров обычно моделируются как case object, в то время как типы листьев с параметрами моделируются как case class.

Когда вам нужно создать экземпляр внутреннего узла дерева типов, просто добавьте искусственный лист и реализуйте его как объект класса case/case.

Вы также можете использовать Enumerations в Scala, но обычно классы корпуса более практичны. Перечисления обычно являются хорошим выбором, когда вам нужно преобразовать заданную строку в соответствующее перечисление. Вы можете сделать это с помощью Enumeration.withName(String).

В ответ от Алексея Романова вы можете увидеть, как применять эти принципы к дереву типов с одним корневым узлом и тремя листовыми узлами.

  1. 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 
+0

Спасибо. Но подумайте о моей ситуации - что бы вы сделали, пытаясь смоделировать иерархию токенов? Будет ок. 50 ключевых слов и т. Д. Я бы также хотел, чтобы под иерархии, такие как Int extends Number, Number, расширяет токены и т. Д. –

+0

См. Вышеизложенное. Под иерархии являются внутренними узлами в дереве типов, и поэтому вы должны моделировать их как признаки. –

+0

Это хорошо. Что вы думаете об этом коде: http://ideone.com/uIdHxj, что я работал над тем временем? –

3
sealed trait Token[+T] { // sealed means it only can be extended in this file 
    def stringValue: String 
    def value: T 
} 

// cast can be avoided if you are happy with extending Token[ReservedSymbol] 
// as in the Java example 
// class instead of trait so that it can have a constructor argument 
sealed class ReservedSymbol[+T <: ReservedSymbol[T]](val stringValue: String) extends Token[T] { 
    def value = this.asInstanceOf[T] 
} 

// no body necessary 
case object ClassSymbol extends ReservedSymbol[ClassSymbol]("class") 
case object VoidSymbol extends ReservedSymbol[VoidSymbol]("void") 

case class IntegerLiteral(val: Int) extends Token[Int] { ... } 
+0

Не могли бы вы дать более длинный пример? У меня будет 100 ключевых слов (не только class и void), а объявление отдельного объекта для каждого символа будет плохой. Вот почему я думал о Enum в Java –

+0

Немного улучшил его. –

Смежные вопросы