我尝试使用包含标签和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-like / immutable-objecty -way保持一致,我认为像这样定义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))
,但是抽象基类没有自动复制构造函数魔法)
答案 0 :(得分: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)
但是,实际上,我不会过于迅速地拒绝在这里使用变量。关于不可变值的推理更容易,但据我所知,你获得的值在解析器中非常孤立。另一个想法是创建一个方法,在最后将所有内容转换为不可变版本,或者如果解析器代码最终将所有数据存储在其他位置,只需将其保留为可变。
另一种方法是使抽象类不是抽象的,而是实际上使它成为一个案例类。您可以从案例类派生类(但是从案例类派生案例类是禁止的,但是)。然后,诀窍是将您可能需要保存的变量数据保存在字段中:
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))