加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

Scala中的内部DSL:没有“,”的列表

发布时间:2020-12-16 18:25:31 所属栏目:安全 来源:网络整理
导读:我正在尝试在 Scala中构建一个内部DSL来表示代数定义.让我们考虑一下这个简化的数据模型: case class Var(name:String)case class Eq(head:Var,body:Var*)case class Definition(name:String,body:Eq*) 例如,一个简单的定义是: val x = Var("x")val y = Va
我正在尝试在 Scala中构建一个内部DSL来表示代数定义.让我们考虑一下这个简化的数据模型:

case class Var(name:String)
case class Eq(head:Var,body:Var*)
case class Definition(name:String,body:Eq*)

例如,一个简单的定义是:

val x = Var("x")
val y = Var("y")
val z = Var("z")
val eq1 = Eq(x,y,z)
val eq2 = Eq(y,x,z)
val defn = Definition("Dummy",eq1,eq2)

我希望有一个内部DSL来表示以下形式的等式:

Dummy {
   x = y z
   y = x z
}

我能得到的最接近的是:

Definition("Dummy") := (
    "x" -> ("y","z")
    "y" -> ("x","z")
)

我遇到的第一个问题是我不能对Definition和Var进行两次隐式转换,因此定义(“Dummy”).然而,主要问题是清单.我不想用任何东西包围它们,例如(),我也不希望它们的元素用逗号分隔.

我想要什么可能使用Scala?如果是的话,有人能告诉我一个简单的方法吗?

解决方法

虽然Scalas语法功能强大,但它不够灵活,无法为符号创建任意分隔符.因此,没有办法留下逗号并仅用空格替换它们.

然而,可以使用宏并在编译时解析具有任意内容的字符串.它不是一个“简单”的解决方案,而是一个有效的解决方案:

object AlgDefDSL {

  import language.experimental.macros

  import scala.reflect.macros.Context

  implicit class DefDSL(sc: StringContext) {
    def dsl(): Definition = macro __dsl_impl
  }

  def __dsl_impl(c: Context)(): c.Expr[Definition] = {
    import c.universe._

    val defn = c.prefix.tree match {
      case Apply(_,List(Apply(_,List(Literal(Constant(s: String)))))) =>

        def toAST[A : TypeTag](xs: Tree*): Tree =
          Apply(
            Select(Ident(typeOf[A].typeSymbol.companionSymbol),newTermName("apply")),xs.toList
          )

        def toVarAST(varObj: Var) =
          toAST[Var](c.literal(varObj.name).tree)

        def toEqAST(eqObj: Eq) =
          toAST[Eq]((eqObj.head +: eqObj.body).map(toVarAST(_)): _*)

        def toDefAST(defObj: Definition) =
          toAST[Definition](c.literal(defObj.name).tree +: defObj.body.map(toEqAST(_)): _*)

        parsers.parse(s) match {
          case parsers.Success(defn,_)  => toDefAST(defn)
          case parsers.NoSuccess(msg,_) => c.abort(c.enclosingPosition,msg)
        }
    }
    c.Expr(defn)
  }

  import scala.util.parsing.combinator.JavaTokenParsers

  private object parsers extends JavaTokenParsers {

    override val whiteSpace = "[ t]*".r

    lazy val newlines =
      opt(rep("n"))

    lazy val varP =
      "[a-z]+".r ^^ Var

    lazy val eqP =
      (varP <~ "=") ~ rep(varP) ^^ {
        case lhs ~ rhs => Eq(lhs,rhs: _*)
      }

    lazy val defHead =
      newlines ~> ("[a-zA-Z]+".r <~ "{") <~ newlines

    lazy val defBody =
      rep(eqP <~ rep("n"))

    lazy val defEnd =
      "}" ~ newlines

    lazy val defP =
      defHead ~ defBody <~ defEnd ^^ {
        case name ~ eqs => Definition(name,eqs: _*)
      }

    def parse(s: String) = parseAll(defP,s)
  }

  case class Var(name: String)
  case class Eq(head: Var,body: Var*)
  case class Definition(name: String,body: Eq*)
}

它可以用于这样的事情:

scala> import AlgDefDSL._
import AlgDefDSL._

scala> dsl"""
     | Dummy {
     |   x = y z
     |   y = x z
     | }
     | """
res12: AlgDefDSL.Definition = Definition(Dummy,WrappedArray(Eq(Var(x),WrappedArray(Var(y),Var(z))),Eq(Var(y),WrappedArray(Var(x),Var(z)))))

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读