1

ラベルと goto を含むコマンドを使用して、小さな言語用のパーサーを作成しようとしています。

...
lazy val cmds  = opt("{")~>rep(cmd<~opt(";"))<~opt("}") ^^ {...}
lazy val cmd = ("if"~boolexpr~"then"~cmds~"else"~cmds 
   ^^ { case _~b~_~c1~_~c2 => IFCMD(boolexpr,c1
 | ident ~":="~numericLit ^^ {case i1~_~v => ASSIGN(i1,v) }
 | "goto" ~>ident ^^ { case l => GOTO(l) }
 | ident~":"~cmd ^^ { case l~_~c  => <APPENDLABELTO_CORE>
...

などはGOTOIFCMD抽象クラスを拡張するケース クラスです。Core

Functional/scala-like/immutable-objecty の方法に合わせて、次のように定義するCoreのは間違っていると考えています。

abstract class Core(var label:Option[String] = None )

しかし、私はその部分を次のように置き換えることができます<APPENDLABELTO_CORE>:

 | ident~":"~cmd ^^ { case l~_~c  => c.label = Some(l); c }

これを行うための「スカッシュな」方法を指摘できる人はいますか?

(試してみましc copy (label=Some(l))たが、抽象基本クラスには自動コピーコンストラクターの魔法がありません)

4

1 に答える 1

4

独自のコピーのようなメソッドを作成することは完全に可能です。

abstract class Core(val label: Option[String]) {
  def set(label: Option[String]): Core
}
class Impl(label: Option[String] = None) extends Core(label) {
  def set(label: Option[String] = this.label) = new Impl(label)
}

このように使用されます:

scala> val i = new Impl
i: Impl = Impl@1930ebb

scala> i.label
res0: Option[String] = None

scala> i.set(label = Some("thing"))
res1: Impl = Impl@b28f30

scala> res1.label
res2: Option[String] = Some(thing)

しかし、実際的には、ここでvarsの使用を却下するのは早すぎません。不変の値について推論する方が簡単ですが、私が知る限り、取得する値はパーサー内でかなり分離されています。別のアイデアは、最後にすべてを不変バージョンに変換するメソッドを作成することです。または、パーサーコードがすべてのデータを他の場所に格納し、変更可能なままにしておく場合もあります。

もう1つの方法は、抽象クラスを抽象ではなく、実際にケースクラスにすることです。ケースクラスからクラスを派生させることができます(ただし、ケースクラスからケースクラスを派生させることはできません)。その場合の秘訣は、フィールドにライブで保存する必要がある可能性のある変数データを作成することです。

abstract class SpecializedStuff { }
case class ParticularStuff(val i: Int) extends SpecializedStuff
case class Core(details: SpecializedStuff, label: Option[String] = None)
class Impl(p: ParticularStuff) extends Core(p)

scala> val i = new Impl( ParticularStuff(5) )
i: Impl = Core(ParticularStuff(5),None)

scala> i.copy(label = Some("thing"))
res0: Core = Core(ParticularStuff(5),Some(thing))
于 2010-08-11T14:54:55.070 に答える