Progress on BDDs: Generating BDD structure (still need codegen)
This commit is contained in:
parent
b87d3a01b0
commit
4bb865e6df
|
@ -19,7 +19,7 @@ object Astral
|
||||||
}
|
}
|
||||||
|
|
||||||
println(
|
println(
|
||||||
BDDCompiler.targets(definition).mkString("\n-----\n")
|
BDDCompiler.bdd(definition)
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,35 @@ package com.astraldb.bdd
|
||||||
|
|
||||||
import com.astraldb.expression._
|
import com.astraldb.expression._
|
||||||
import com.astraldb.spec._
|
import com.astraldb.spec._
|
||||||
|
import com.astraldb.codegen.Code
|
||||||
|
|
||||||
sealed trait BDD
|
sealed trait BDD
|
||||||
|
{
|
||||||
|
def code: Code
|
||||||
|
override def toString(): String =
|
||||||
|
code.toString
|
||||||
|
}
|
||||||
|
|
||||||
case class PickByType(
|
case class PickByType(
|
||||||
path: Seq[Int],
|
path: Seq[Int],
|
||||||
subtrees: Map[Type, BDD]
|
subtrees: Map[Type, BDD]
|
||||||
) extends BDD
|
) extends BDD
|
||||||
|
{
|
||||||
|
def code =
|
||||||
|
Code.Parens(
|
||||||
|
left = s"@[${path.mkString(",")}]:PickByType {",
|
||||||
|
right = s"}",
|
||||||
|
body = Code.Block(
|
||||||
|
subtrees.map { case (t, andThen) =>
|
||||||
|
Code.Parens(
|
||||||
|
left = s"case $t",
|
||||||
|
right = "",
|
||||||
|
body = andThen.code
|
||||||
|
)
|
||||||
|
}.toSeq
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
case class PickByMatch(
|
case class PickByMatch(
|
||||||
path: Seq[Int],
|
path: Seq[Int],
|
||||||
|
@ -16,7 +38,41 @@ case class PickByMatch(
|
||||||
ifMatched: BDD,
|
ifMatched: BDD,
|
||||||
ifNotMatched: BDD,
|
ifNotMatched: BDD,
|
||||||
) extends BDD
|
) extends BDD
|
||||||
|
{
|
||||||
|
def code =
|
||||||
|
Code.IfThenElse(
|
||||||
|
Code.Literal(Match.Path(path, matcher).toString),
|
||||||
|
ifMatched.code,
|
||||||
|
ifNotMatched.code
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
case class BindAnExpression(
|
||||||
|
symbol: String,
|
||||||
|
expression: Expression,
|
||||||
|
andThen: BDD
|
||||||
|
) extends BDD
|
||||||
|
{
|
||||||
|
def code =
|
||||||
|
Code.Parens(
|
||||||
|
left = s"let $symbol <- $expression",
|
||||||
|
right = "",
|
||||||
|
body = andThen.code
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
case class Rewrite(
|
case class Rewrite(
|
||||||
|
label: String,
|
||||||
rewrite: Expression
|
rewrite: Expression
|
||||||
) extends BDD
|
) extends BDD
|
||||||
|
{
|
||||||
|
def code =
|
||||||
|
Code.Literal(s"Rewrite with $label")
|
||||||
|
}
|
||||||
|
|
||||||
|
case object NoRewrite extends BDD
|
||||||
|
{
|
||||||
|
def code =
|
||||||
|
Code.Literal(s"No Rewrite Possible")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
28
astral/compiler/src/com/astraldb/bdd/Candidate.scala
Normal file
28
astral/compiler/src/com/astraldb/bdd/Candidate.scala
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package com.astraldb.bdd
|
||||||
|
|
||||||
|
import com.astraldb.spec.Match
|
||||||
|
import com.astraldb.expression.Expression
|
||||||
|
import com.astraldb.spec.Type
|
||||||
|
|
||||||
|
sealed trait CandidateStep
|
||||||
|
|
||||||
|
case class CheckTypeStep(path: Pathed.Path, check: Type) extends CandidateStep
|
||||||
|
object CheckTypeStep
|
||||||
|
{
|
||||||
|
def apply(pathed: Pathed[Type]): CheckTypeStep =
|
||||||
|
CheckTypeStep(pathed.path, pathed.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
case class CheckMatchStep(path: Pathed.Path, matcher: Match) extends CandidateStep
|
||||||
|
object CheckMatchStep
|
||||||
|
{
|
||||||
|
def apply(pathed: Pathed[Match]): CheckMatchStep =
|
||||||
|
CheckMatchStep(pathed.path, pathed.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
case class BindExpressionStep(symbol: String, expression: Expression) extends CandidateStep
|
||||||
|
object BindExpressionStep
|
||||||
|
{
|
||||||
|
def apply(binding: (String, Expression)): BindExpressionStep =
|
||||||
|
BindExpressionStep(binding._1, binding._2)
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package com.astraldb.bdd
|
||||||
import com.astraldb.spec.Definition
|
import com.astraldb.spec.Definition
|
||||||
import com.astraldb.spec.Match
|
import com.astraldb.spec.Match
|
||||||
import com.astraldb.spec.Type
|
import com.astraldb.spec.Type
|
||||||
|
import com.astraldb.expression.Expression
|
||||||
|
|
||||||
object Compiler
|
object Compiler
|
||||||
{
|
{
|
||||||
|
@ -92,7 +93,7 @@ object Compiler
|
||||||
disjunctify(matcher)
|
disjunctify(matcher)
|
||||||
}
|
}
|
||||||
|
|
||||||
def targets(schema: Definition): Seq[Target] =
|
def getTargets(schema: Definition): Seq[Target] =
|
||||||
{
|
{
|
||||||
schema.rules.flatMap { rule =>
|
schema.rules.flatMap { rule =>
|
||||||
val clauses =
|
val clauses =
|
||||||
|
@ -107,4 +108,164 @@ object Compiler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def getSanitizedTargets(schema: Definition): Seq[Target] =
|
||||||
|
{
|
||||||
|
var targets = getTargets(schema)
|
||||||
|
|
||||||
|
// Sanitize expression bindings via alpha renaming and
|
||||||
|
// common subexpression elimination
|
||||||
|
// * If two expressions are the same, they should
|
||||||
|
// have the same name.
|
||||||
|
|
||||||
|
val duplicateExprs: Map[Expression, String] =
|
||||||
|
targets.flatMap { _.exprBindings }
|
||||||
|
.toSet.toSeq
|
||||||
|
.groupBy { _._2 }
|
||||||
|
.filter { _._2.size > 1 }
|
||||||
|
.mapValues { _.map { _._1 }.head }
|
||||||
|
.toMap
|
||||||
|
|
||||||
|
targets = targets.map { target =>
|
||||||
|
val repair =
|
||||||
|
target.exprBindings
|
||||||
|
.filter { b => duplicateExprs contains b._2 }
|
||||||
|
.map { b =>
|
||||||
|
b._1 -> duplicateExprs(b._2)
|
||||||
|
}
|
||||||
|
.filterNot { a => a._1 != a._2 }
|
||||||
|
if(repair.isEmpty){ target }
|
||||||
|
else {
|
||||||
|
target.rename(repair:_*)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// * If two expressions are different, they should
|
||||||
|
// have different names.
|
||||||
|
val replacementNames: Map[String, Map[Expression, String]] =
|
||||||
|
targets.flatMap { _.exprBindings }
|
||||||
|
.toSet.toSeq
|
||||||
|
.groupBy { _._1 }
|
||||||
|
.filter { _._2.size > 1 }
|
||||||
|
.map { case (name:String, exprs:Seq[(String, Expression)]) =>
|
||||||
|
name ->
|
||||||
|
exprs.map { _._2 }
|
||||||
|
.zipWithIndex
|
||||||
|
.toMap
|
||||||
|
.mapValues { name + "_" + _ }
|
||||||
|
.toMap
|
||||||
|
}
|
||||||
|
.toMap
|
||||||
|
|
||||||
|
targets = targets.map { target =>
|
||||||
|
val repair =
|
||||||
|
target.exprBindings
|
||||||
|
.flatMap { case (name, expr) =>
|
||||||
|
replacementNames.get(name)
|
||||||
|
.flatMap { _.get(expr).map { newName =>
|
||||||
|
name -> newName
|
||||||
|
} }
|
||||||
|
}
|
||||||
|
if(repair.isEmpty) { target }
|
||||||
|
else { target.rename(repair:_*) }
|
||||||
|
}
|
||||||
|
|
||||||
|
return targets
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def bdd(schema: Definition): BDD =
|
||||||
|
{
|
||||||
|
println(getSanitizedTargets(schema).mkString("\n---\n"))
|
||||||
|
def stepBdd(targets: Seq[Target], state: State): BDD =
|
||||||
|
{
|
||||||
|
val success = targets.find { _.successfulOn(state) }
|
||||||
|
|
||||||
|
if(success.isDefined)
|
||||||
|
{
|
||||||
|
Rewrite(success.get.label, success.get.rewrite)
|
||||||
|
} else {
|
||||||
|
val activeTargets =
|
||||||
|
targets.filter { _.canBeSuccessfulOn(state) }
|
||||||
|
|
||||||
|
if(activeTargets.isEmpty){
|
||||||
|
NoRewrite
|
||||||
|
} else {
|
||||||
|
|
||||||
|
val candidates:Seq[CandidateStep] =
|
||||||
|
activeTargets
|
||||||
|
.flatMap { t =>
|
||||||
|
val c = t.candidates(state)
|
||||||
|
assert(!c.isEmpty, s"Target ${t.label} in state \n$state\n is not successful or failed, but has no candidates for progress.")
|
||||||
|
/* return */ c
|
||||||
|
}
|
||||||
|
|
||||||
|
val bestCheckTypeStep =
|
||||||
|
candidates.collect {
|
||||||
|
case check:CheckTypeStep =>
|
||||||
|
check.path -> check
|
||||||
|
}
|
||||||
|
.groupBy { _._1 }
|
||||||
|
.toSeq
|
||||||
|
.maxByOption { _._2.size }
|
||||||
|
|
||||||
|
if(bestCheckTypeStep.isDefined)
|
||||||
|
{
|
||||||
|
val path: Pathed.Path =
|
||||||
|
bestCheckTypeStep.get._1
|
||||||
|
val types:Set[Type] =
|
||||||
|
bestCheckTypeStep.get._2.map { _._2.check }.toSet
|
||||||
|
|
||||||
|
println(s"Checking path: [${path.mkString(", ")}] for ${types.mkString(", ")}")
|
||||||
|
|
||||||
|
PickByType(
|
||||||
|
path,
|
||||||
|
types.toSeq.map { t =>
|
||||||
|
t -> stepBdd(activeTargets, state.withType(path, t))
|
||||||
|
}.toMap
|
||||||
|
)
|
||||||
|
} else { // no advancement through a best check type
|
||||||
|
|
||||||
|
val nonCheckTypeCandidates =
|
||||||
|
candidates.filterNot { _.isInstanceOf[CheckTypeStep] }
|
||||||
|
|
||||||
|
assert(!nonCheckTypeCandidates.isEmpty,
|
||||||
|
s"No possible candidates to make progress: \nstate=$state\ntargets=\n${activeTargets.mkString("\n---\n")}\ncandidates=$candidates"
|
||||||
|
)
|
||||||
|
|
||||||
|
val bestOtherCandidate =
|
||||||
|
nonCheckTypeCandidates
|
||||||
|
.groupBy { x => x }
|
||||||
|
.maxBy { _._2.size }
|
||||||
|
._1
|
||||||
|
|
||||||
|
|
||||||
|
bestOtherCandidate match {
|
||||||
|
case _:CheckTypeStep => assert(false, "We filtered out all the check type steps by this point")
|
||||||
|
case CheckMatchStep(path, matcher) =>
|
||||||
|
PickByMatch(
|
||||||
|
path = path,
|
||||||
|
matcher = matcher,
|
||||||
|
ifMatched =
|
||||||
|
stepBdd(activeTargets,
|
||||||
|
state.withSuccessfulMatch(path, matcher)),
|
||||||
|
ifNotMatched =
|
||||||
|
stepBdd(activeTargets,
|
||||||
|
state.withFailedMatch(path, matcher)),
|
||||||
|
)
|
||||||
|
case BindExpressionStep(symbol, expression) =>
|
||||||
|
BindAnExpression(symbol, expression,
|
||||||
|
stepBdd(activeTargets,
|
||||||
|
state.withBinding(symbol)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stepBdd(getSanitizedTargets(schema), State.empty(schema))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,8 +1,41 @@
|
||||||
package com.astraldb.bdd
|
package com.astraldb.bdd
|
||||||
|
|
||||||
|
|
||||||
case class Pathed[T](path: Seq[Int], value: T)
|
case class Pathed[T](path: Pathed.Path, value: T)
|
||||||
{
|
{
|
||||||
override def toString(): String =
|
override def toString(): String =
|
||||||
s"@[${path.mkString(", ")}]: $value"
|
s"@[${path.mkString(", ")}]: $value"
|
||||||
|
|
||||||
|
def tuple = (path, value)
|
||||||
|
|
||||||
|
def pathInSet(set: Set[Pathed.Path]) =
|
||||||
|
Pathed.selfAndAncestorsInSet(path, set)
|
||||||
|
|
||||||
|
def ancestorsInSet(set: Set[Pathed.Path]) =
|
||||||
|
Pathed.ancestorsInSet(path, set)
|
||||||
|
}
|
||||||
|
|
||||||
|
object Pathed
|
||||||
|
{
|
||||||
|
type Path = Seq[Int]
|
||||||
|
|
||||||
|
def selfAndAncestorsInSet(path: Path, set: Set[Path]): Boolean =
|
||||||
|
{
|
||||||
|
if(!set(Seq.empty)){ false }
|
||||||
|
else {
|
||||||
|
path.foldLeft(Some(Seq[Int]()):Option[Seq[Int]]) {
|
||||||
|
case (Some(seq), elem) =>
|
||||||
|
val newSeq = seq :+ elem
|
||||||
|
if(set contains (newSeq)) { Some(newSeq) } else { None }
|
||||||
|
|
||||||
|
case (None, _) => None
|
||||||
|
}.isDefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def ancestorsInSet(path:Path, set: Set[Path]): Boolean =
|
||||||
|
if(path.isEmpty){ true }
|
||||||
|
else {
|
||||||
|
selfAndAncestorsInSet(path.dropRight(1), set)
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -4,6 +4,42 @@ import com.astraldb.expression._
|
||||||
import com.astraldb.spec._
|
import com.astraldb.spec._
|
||||||
|
|
||||||
case class State(
|
case class State(
|
||||||
types: Pathed[Type],
|
types: Set[Pathed[Type]],
|
||||||
matchers: Pathed[Match]
|
passedMatchers: Set[Pathed[Match]],
|
||||||
|
failedMatchers: Set[Pathed[Match]],
|
||||||
|
bindings: Set[String]
|
||||||
)
|
)
|
||||||
|
{
|
||||||
|
assert((passedMatchers & failedMatchers).isEmpty, "A matcher can't be both successful and failed.")
|
||||||
|
|
||||||
|
lazy val typeOf = types.map { _.tuple }.toMap
|
||||||
|
|
||||||
|
def withType(path: Pathed.Path, value: Type): State =
|
||||||
|
copy(types = types ++ Set(Pathed(path, value)))
|
||||||
|
|
||||||
|
def withSuccessfulMatch(path: Pathed.Path, matcher: Match): State =
|
||||||
|
copy(passedMatchers = passedMatchers ++ Set(Pathed(path, matcher)))
|
||||||
|
|
||||||
|
def withFailedMatch(path: Pathed.Path, matcher: Match): State =
|
||||||
|
copy(failedMatchers = failedMatchers ++ Set(Pathed(path, matcher)))
|
||||||
|
|
||||||
|
def withBinding(symbol: String): State =
|
||||||
|
copy(bindings = bindings ++ Set(symbol))
|
||||||
|
|
||||||
|
override def toString(): String =
|
||||||
|
"=== Types ===\n"++
|
||||||
|
types.mkString("\n")++
|
||||||
|
"\n=== Passed Matchers ===\n"++
|
||||||
|
passedMatchers.mkString("\n")++
|
||||||
|
"\n=== Failed Matchers ===\n"++
|
||||||
|
failedMatchers.mkString("\n")++
|
||||||
|
"\n=== Bindings ===\n"++
|
||||||
|
bindings.mkString(",")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object State
|
||||||
|
{
|
||||||
|
def empty(schema: Definition) =
|
||||||
|
State(Set.empty, Set.empty, Set.empty, schema.globals.keySet)
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package com.astraldb.bdd
|
||||||
|
|
||||||
import com.astraldb.expression._
|
import com.astraldb.expression._
|
||||||
import com.astraldb.spec._
|
import com.astraldb.spec._
|
||||||
|
import scala.util.Try
|
||||||
|
|
||||||
case class Target(
|
case class Target(
|
||||||
label: String,
|
label: String,
|
||||||
|
@ -29,12 +30,122 @@ case class Target(
|
||||||
|
|
||||||
def withoutUnusedBindings =
|
def withoutUnusedBindings =
|
||||||
{
|
{
|
||||||
val refs = (rewrite.references ++ matchers.flatMap { _.value.references }).map { _.v }
|
val refs: Set[String] = (
|
||||||
|
rewrite.references ++
|
||||||
|
matchers.flatMap { _.value.references }).map { _.v } ++
|
||||||
|
exprBindings.flatMap { _._2.references }.map { _.v}
|
||||||
|
|
||||||
copy(
|
copy(
|
||||||
bindings = bindings.filter { b => refs(b.value) }
|
bindings = bindings.filter { b => refs(b.value) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def candidates(state: State): Seq[CandidateStep] =
|
||||||
|
{
|
||||||
|
val requestedTypedPaths = types.map { _.path }.toSet
|
||||||
|
val typedPaths = state.typeOf.keySet
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variable bindings that have had all of their prerequisites filled.
|
||||||
|
*/
|
||||||
|
val validBindings:Set[Var] = (
|
||||||
|
// bindings that were computed by a prior step
|
||||||
|
state.bindings.map { Var(_) } ++
|
||||||
|
// path bindings that have been fully typed
|
||||||
|
bindings.filter { b =>
|
||||||
|
// If we want the binding to be typed, we need to check up to the type of the binding
|
||||||
|
if(requestedTypedPaths(b.path)) { b.pathInSet(typedPaths) }
|
||||||
|
// If the binding itself is not typed, then we just need its ancestors bound
|
||||||
|
else { b.ancestorsInSet(typedPaths) }
|
||||||
|
}.map { b => Var(b.value) }
|
||||||
|
).toSet
|
||||||
|
|
||||||
|
// println(s"Valid bindings: $validBindings")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expression bindings that could potentially be run right now.
|
||||||
|
*/
|
||||||
|
val candidateBindings =
|
||||||
|
exprBindings
|
||||||
|
// Don't re-compute bindings that are already available
|
||||||
|
.filterNot { b => validBindings(Var(b._1)) }
|
||||||
|
// A binding needs all of its referenced variables to be bound
|
||||||
|
.filter { _._2.references.forall { validBindings(_) } }
|
||||||
|
|
||||||
|
// println(s"Typed Paths: $typedPaths")
|
||||||
|
|
||||||
|
val candidateTypings =
|
||||||
|
types
|
||||||
|
// Don't re-check typings that are already available
|
||||||
|
.filterNot { t => typedPaths(t.path) }
|
||||||
|
// All ancestors need to be typed
|
||||||
|
.filter { _.ancestorsInSet(typedPaths) }
|
||||||
|
|
||||||
|
// println(s"Candidate Typings: $candidateTypings")
|
||||||
|
|
||||||
|
val candidateMatchers =
|
||||||
|
matchers
|
||||||
|
// Don't re-check matchers that have already been computed
|
||||||
|
.filterNot { m =>
|
||||||
|
// println("m1:"+m);
|
||||||
|
state.passedMatchers(m) }
|
||||||
|
// All ancestors need to be typed
|
||||||
|
.filter { m =>
|
||||||
|
// println("m2:"+m);
|
||||||
|
m.ancestorsInSet(typedPaths) }
|
||||||
|
// A matcher needs all of its referenced variables to be bound
|
||||||
|
.filter { m =>
|
||||||
|
// println("m3:"+m+"\n"+m.value.references);
|
||||||
|
m.value.references.forall { validBindings(_) } }
|
||||||
|
|
||||||
|
// println(s"Candidate Matchers: $candidateMatchers")
|
||||||
|
|
||||||
|
(candidateTypings.map { CheckTypeStep(_) }: Seq[CandidateStep]) ++
|
||||||
|
candidateMatchers.map { CheckMatchStep(_) } ++
|
||||||
|
candidateBindings.map { BindExpressionStep(_) }
|
||||||
|
}
|
||||||
|
|
||||||
|
def canBeSuccessfulOn(state: State): Boolean =
|
||||||
|
types.forall { t =>
|
||||||
|
state.typeOf.get(t.path)
|
||||||
|
.map { _ == t.value }
|
||||||
|
.getOrElse { true }
|
||||||
|
} && matchers.forall { m =>
|
||||||
|
!(state.failedMatchers contains m)
|
||||||
|
}
|
||||||
|
|
||||||
|
def successfulOn(state: State): Boolean =
|
||||||
|
types.forall { t =>
|
||||||
|
state.typeOf.get(t.path)
|
||||||
|
.map { _ == t.value }
|
||||||
|
.getOrElse { false }
|
||||||
|
} && matchers.forall { m =>
|
||||||
|
(state.passedMatchers contains m)
|
||||||
|
} && exprBindings.forall { e =>
|
||||||
|
state.bindings.contains(e._1)
|
||||||
|
}
|
||||||
|
|
||||||
|
def rename(replacements: (String, String)*): Target =
|
||||||
|
{
|
||||||
|
val replacementMap = replacements.toMap
|
||||||
|
copy(
|
||||||
|
rewrite = rewrite.rename(replacementMap),
|
||||||
|
bindings = bindings.map { b =>
|
||||||
|
replacementMap.get(b.value)
|
||||||
|
.map { r => b.copy(value = r) }
|
||||||
|
.getOrElse(b)
|
||||||
|
},
|
||||||
|
exprBindings = exprBindings.map { case (name, expr) =>
|
||||||
|
( replacementMap.getOrElse(name, name),
|
||||||
|
expr.rename(replacementMap)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
matchers = matchers.map { m =>
|
||||||
|
m.copy(value = m.value.rename(replacementMap))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override def toString(): String =
|
override def toString(): String =
|
||||||
label+
|
label+
|
||||||
(if(types.isEmpty){ "" } else {
|
(if(types.isEmpty){ "" } else {
|
||||||
|
|
|
@ -79,11 +79,18 @@ sealed abstract class Expression
|
||||||
|
|
||||||
def children: Seq[Expression]
|
def children: Seq[Expression]
|
||||||
def reassemble(in: Seq[Expression]): Expression
|
def reassemble(in: Seq[Expression]): Expression
|
||||||
def rebuild(fn:(Expression => Expression)): Expression =
|
def transform(fn: PartialFunction[Expression,Expression]): Expression =
|
||||||
reassemble(children.map { fn(_) })
|
reassemble(children.map { _.transform(fn) }).map(fn)
|
||||||
|
def map(fn: PartialFunction[Expression,Expression]): Expression =
|
||||||
|
fn.applyOrElse(this, _ => this)
|
||||||
|
|
||||||
def references: Set[Var] =
|
def references: Set[Var] =
|
||||||
children.flatMap { _.references }.toSet
|
children.flatMap { _.references }.toSet
|
||||||
|
|
||||||
|
def rename(newNames: Map[String, String]): Expression =
|
||||||
|
transform {
|
||||||
|
case Var(v) if newNames contains v => Var(newNames(v))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////// Constants ////////////////////////////
|
///////////////////////// Constants ////////////////////////////
|
||||||
|
|
|
@ -26,8 +26,11 @@ sealed trait Match
|
||||||
def orSeq: Seq[Match] =
|
def orSeq: Seq[Match] =
|
||||||
this match { case Match.Or(children) => children; case _ => Seq(this) }
|
this match { case Match.Or(children) => children; case _ => Seq(this) }
|
||||||
|
|
||||||
|
def expressions: Seq[Expression]
|
||||||
|
|
||||||
def references: Set[Var] =
|
def references: Set[Var] =
|
||||||
children.flatMap { _.references }.toSet
|
children.flatMap { _.references }.toSet ++
|
||||||
|
expressions.flatMap { _.references }.toSet
|
||||||
|
|
||||||
def and(other: Match) =
|
def and(other: Match) =
|
||||||
Match.And(andSeq ++ other.andSeq)
|
Match.And(andSeq ++ other.andSeq)
|
||||||
|
@ -48,6 +51,9 @@ sealed trait Match
|
||||||
|
|
||||||
def mapChildren(f: Function[Match, Match]): Match =
|
def mapChildren(f: Function[Match, Match]): Match =
|
||||||
reassemble(children.map(f))
|
reassemble(children.map(f))
|
||||||
|
|
||||||
|
def rename(newNames: Map[String, String]): Match =
|
||||||
|
reassemble(children.map { _.rename(newNames) })
|
||||||
}
|
}
|
||||||
object Match
|
object Match
|
||||||
{
|
{
|
||||||
|
@ -73,6 +79,7 @@ object Match
|
||||||
s"Not(\n$prefix${child.toString(prefix+" ")}\n$prefix)"
|
s"Not(\n$prefix${child.toString(prefix+" ")}\n$prefix)"
|
||||||
def children = Seq(child)
|
def children = Seq(child)
|
||||||
def reassemble(in: Seq[Match]) = copy(child = in(0))
|
def reassemble(in: Seq[Match]) = copy(child = in(0))
|
||||||
|
def expressions: Seq[Expression] = Seq.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -87,6 +94,7 @@ object Match
|
||||||
def toString(prefix: String): String =
|
def toString(prefix: String): String =
|
||||||
s"And(\n$prefix ${children.map { _.toString(prefix+" ")}.mkString(",\n"+prefix+" ")}\n$prefix)"
|
s"And(\n$prefix ${children.map { _.toString(prefix+" ")}.mkString(",\n"+prefix+" ")}\n$prefix)"
|
||||||
def reassemble(in: Seq[Match]) = copy(children = in)
|
def reassemble(in: Seq[Match]) = copy(children = in)
|
||||||
|
def expressions: Seq[Expression] = Seq.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -104,6 +112,7 @@ object Match
|
||||||
def toString(prefix: String): String =
|
def toString(prefix: String): String =
|
||||||
s"Or(\n$prefix ${children.map { _.toString(prefix+" ")}.mkString(",\n"+prefix+" ")}\n$prefix)"
|
s"Or(\n$prefix ${children.map { _.toString(prefix+" ")}.mkString(",\n"+prefix+" ")}\n$prefix)"
|
||||||
def reassemble(in: Seq[Match]) = copy(children = in)
|
def reassemble(in: Seq[Match]) = copy(children = in)
|
||||||
|
def expressions: Seq[Expression] = Seq.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -128,6 +137,7 @@ object Match
|
||||||
def toString(prefix: String): String =
|
def toString(prefix: String): String =
|
||||||
s"{$nodeLabel}(\n$prefix ${children.map { _.toString(prefix+" ")}.mkString(",\n"+prefix+" ")}\n$prefix)"
|
s"{$nodeLabel}(\n$prefix ${children.map { _.toString(prefix+" ")}.mkString(",\n"+prefix+" ")}\n$prefix)"
|
||||||
def reassemble(in: Seq[Match]) = copy(children = in)
|
def reassemble(in: Seq[Match]) = copy(children = in)
|
||||||
|
def expressions: Seq[Expression] = Seq.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -145,6 +155,7 @@ object Match
|
||||||
s"{$nodeType}"
|
s"{$nodeType}"
|
||||||
def children: Seq[Match] = Seq.empty
|
def children: Seq[Match] = Seq.empty
|
||||||
def reassemble(in: Seq[Match]): Match = this
|
def reassemble(in: Seq[Match]): Match = this
|
||||||
|
def expressions: Seq[Expression] = Seq.empty
|
||||||
}
|
}
|
||||||
object OfType
|
object OfType
|
||||||
{
|
{
|
||||||
|
@ -167,6 +178,7 @@ object Match
|
||||||
s"Exact(${pattern})"
|
s"Exact(${pattern})"
|
||||||
def children: Seq[Match] = Seq.empty
|
def children: Seq[Match] = Seq.empty
|
||||||
def reassemble(in: Seq[Match]): Match = this
|
def reassemble(in: Seq[Match]): Match = this
|
||||||
|
def expressions: Seq[Expression] = Seq.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -194,7 +206,7 @@ object Match
|
||||||
s"@[${path.mkString(",")}](\n$prefix ${pattern.toString(prefix+" ")}\n$prefix)"
|
s"@[${path.mkString(",")}](\n$prefix ${pattern.toString(prefix+" ")}\n$prefix)"
|
||||||
def children: Seq[Match] = Seq(pattern)
|
def children: Seq[Match] = Seq(pattern)
|
||||||
def reassemble(in: Seq[Match]): Match = copy(pattern = in(0))
|
def reassemble(in: Seq[Match]): Match = copy(pattern = in(0))
|
||||||
|
def expressions: Seq[Expression] = Seq.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -211,6 +223,7 @@ object Match
|
||||||
s"Recursive(\n$prefix ${pattern.toString(prefix+" ")}\n$prefix)"
|
s"Recursive(\n$prefix ${pattern.toString(prefix+" ")}\n$prefix)"
|
||||||
def children: Seq[Match] = Seq(pattern)
|
def children: Seq[Match] = Seq(pattern)
|
||||||
def reassemble(in: Seq[Match]): Match = copy(pattern = in(0))
|
def reassemble(in: Seq[Match]): Match = copy(pattern = in(0))
|
||||||
|
def expressions: Seq[Expression] = Seq.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -231,6 +244,12 @@ object Match
|
||||||
}
|
}
|
||||||
def children: Seq[Match] = Seq(pattern)
|
def children: Seq[Match] = Seq(pattern)
|
||||||
def reassemble(in: Seq[Match]): Match = copy(pattern = in(0))
|
def reassemble(in: Seq[Match]): Match = copy(pattern = in(0))
|
||||||
|
def expressions: Seq[Expression] = Seq.empty
|
||||||
|
override def rename(newNames: Map[String, String]): Match =
|
||||||
|
Bind(
|
||||||
|
symbol = newNames.getOrElse(symbol, symbol),
|
||||||
|
pattern.rename(newNames)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -244,6 +263,7 @@ object Match
|
||||||
"_"
|
"_"
|
||||||
def children: Seq[Match] = Seq.empty
|
def children: Seq[Match] = Seq.empty
|
||||||
def reassemble(in: Seq[Match]): Match = this
|
def reassemble(in: Seq[Match]): Match = this
|
||||||
|
def expressions: Seq[Expression] = Seq.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -257,6 +277,7 @@ object Match
|
||||||
"Fail!"
|
"Fail!"
|
||||||
def children: Seq[Match] = Seq.empty
|
def children: Seq[Match] = Seq.empty
|
||||||
def reassemble(in: Seq[Match]): Match = this
|
def reassemble(in: Seq[Match]): Match = this
|
||||||
|
def expressions: Seq[Expression] = Seq.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -276,6 +297,9 @@ object Match
|
||||||
def children: Seq[Match] = Seq.empty
|
def children: Seq[Match] = Seq.empty
|
||||||
def reassemble(in: Seq[Match]): Match = this
|
def reassemble(in: Seq[Match]): Match = this
|
||||||
override def references = super.references ++ Set(Var(symbol))
|
override def references = super.references ++ Set(Var(symbol))
|
||||||
|
def expressions: Seq[Expression] = Seq.empty
|
||||||
|
override def rename(newNames: Map[String, String]): Match =
|
||||||
|
copy(symbol = newNames.getOrElse(symbol, symbol))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -295,6 +319,12 @@ object Match
|
||||||
def children: Seq[Match] = Seq(pattern)
|
def children: Seq[Match] = Seq(pattern)
|
||||||
def reassemble(in: Seq[Match]): Match = copy(pattern = in(0))
|
def reassemble(in: Seq[Match]): Match = copy(pattern = in(0))
|
||||||
override def references = super.references ++ Set(Var(symbol))
|
override def references = super.references ++ Set(Var(symbol))
|
||||||
|
def expressions: Seq[Expression] = Seq.empty
|
||||||
|
override def rename(newNames: Map[String, String]): Match =
|
||||||
|
copy(
|
||||||
|
symbol = newNames.getOrElse(symbol, symbol),
|
||||||
|
pattern = pattern.rename(newNames)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -317,6 +347,7 @@ object Match
|
||||||
s"@[*](\n$prefix ${pattern.toString(prefix+" ")}\n$prefix)"
|
s"@[*](\n$prefix ${pattern.toString(prefix+" ")}\n$prefix)"
|
||||||
def children: Seq[Match] = Seq(pattern)
|
def children: Seq[Match] = Seq(pattern)
|
||||||
def reassemble(in: Seq[Match]): Match = copy(pattern = in(0))
|
def reassemble(in: Seq[Match]): Match = copy(pattern = in(0))
|
||||||
|
def expressions: Seq[Expression] = Seq.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -334,6 +365,9 @@ object Match
|
||||||
def children: Seq[Match] = Seq.empty
|
def children: Seq[Match] = Seq.empty
|
||||||
def reassemble(in: Seq[Match]): Match = this
|
def reassemble(in: Seq[Match]): Match = this
|
||||||
override def references = super.references ++ op.references
|
override def references = super.references ++ op.references
|
||||||
|
def expressions: Seq[Expression] = Seq(op)
|
||||||
|
override def rename(newNames: Map[String, String]): Match =
|
||||||
|
Test(op.rename(newNames))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -348,5 +382,11 @@ object Match
|
||||||
def children: Seq[Match] = Seq.empty
|
def children: Seq[Match] = Seq.empty
|
||||||
def reassemble(in: Seq[Match]): Match = this
|
def reassemble(in: Seq[Match]): Match = this
|
||||||
override def references = super.references ++ op.references
|
override def references = super.references ++ op.references
|
||||||
|
def expressions: Seq[Expression] = Seq(op)
|
||||||
|
override def rename(newNames: Map[String, String]): Match =
|
||||||
|
BindExpression(
|
||||||
|
symbol = newNames.getOrElse(symbol, symbol),
|
||||||
|
op = op.rename(newNames)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue