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>
...

GOTO等是扩展抽象类的IFCMD案例类Core

为了与功能/类似scala/不可变对象的方式保持一致,我认为这样定义Core错误的:

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

但可以让我用以下内容替换部分<APPENDLABELTO_CORE>

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

谁能指出“scalaish”的方式来做到这一点?

(我试过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。对不可变值进行推理更容易,但据我所知,您得到的值在解析器中被很好地隔离了。另一种想法是创建一个方法,在最后将所有内容转换为不可变版本,或者如果解析器代码最终将所有数据存储在其他地方,只是让它保持可变。

另一种方法是使抽象类不是抽象的,而是实际上使它成为一个案例类。您可以从案例类派生类(但是,从案例类派生案例类是不可以的)。然后,诀窍是使您可能需要在字段中保存的可变数据:

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 回答