summaryrefslogtreecommitdiff
path: root/src/main/scala/Chisel/Parameters.scala
diff options
context:
space:
mode:
authorJim Lawson2015-05-11 10:04:01 -0700
committerJim Lawson2015-07-24 15:50:53 -0700
commitb208bfb5691c7b5921dd47d0b599726872acd1cd (patch)
tree5d8695f13db41a807622dfdc93c1b6841911acc8 /src/main/scala/Chisel/Parameters.scala
parentcaa7602b878c03c47fd263550e37715f1a67f854 (diff)
move source files under Chisel folder - eclipse compatibility
Diffstat (limited to 'src/main/scala/Chisel/Parameters.scala')
-rw-r--r--src/main/scala/Chisel/Parameters.scala644
1 files changed, 644 insertions, 0 deletions
diff --git a/src/main/scala/Chisel/Parameters.scala b/src/main/scala/Chisel/Parameters.scala
new file mode 100644
index 00000000..9a117ee0
--- /dev/null
+++ b/src/main/scala/Chisel/Parameters.scala
@@ -0,0 +1,644 @@
+/*
+ Constructing Hardware in a Scala Embedded Language, Copyright (c) 2014, The
+ Regents of the University of California, through Lawrence Berkeley National
+ Laboratory (subject to receipt of any required approvals from the U.S. Dept.
+ of Energy). All rights reserved.
+
+ If you have questions about your rights to use or distribute this software,
+ please contact Berkeley Lab's Technology Transfer Department at TTD@lbl.gov.
+
+ NOTICE. This software is owned by the U.S. Department of Energy. As such,
+ the U.S. Government has been granted for itself and others acting on its
+ behalf a paid-up, nonexclusive, irrevocable, worldwide license in the Software
+ to reproduce, prepare derivative works, and perform publicly and display
+ publicly. Beginning five (5) years after the date permission to assert
+ copyright is obtained from the U.S. Department of Energy, and subject to any
+ subsequent five (5) year renewals, the U.S. Government is granted for itself
+ and others acting on its behalf a paid-up, nonexclusive, irrevocable,
+ worldwide license in the Software to reproduce, prepare derivative works,
+ distribute copies to the public, perform publicly and display publicly, and to
+ permit others to do so.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ (1) Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ (3) Neither the name of the University of California, Lawrence Berkeley
+ National Laboratory, U.S. Dept. of Energy nor the names of its contributors
+ may be used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ You are under no obligation whatsoever to provide any bug fixes, patches, or
+ upgrades to the features, functionality or performance of the source code
+ ("Enhancements") to anyone; however, if you choose to make your Enhancements
+ available either publicly, or directly to Lawrence Berkeley National
+ Laboratory, without imposing a separate written license agreement for such
+ Enhancements, then you hereby grant the following license: a non-exclusive,
+ royalty-free perpetual license to install, use, modify, prepare derivative
+ works, incorporate into other computer software, distribute, and sublicense
+ such enhancements or derivative works thereof, in binary and source code form.
+
+ Authors: J. Bachan, A. Izraelevitz, H. Cook
+*/
+
+package Chisel
+
+import scala.collection.immutable.{Seq=>Seq, Iterable=>Iterable}
+import scala.{collection=>readonly}
+import scala.collection.mutable
+
+// Convention: leading _'s on names means private to the outside world
+// but accessible to anything in this file.
+
+abstract trait UsesParameters {
+ def params: Parameters
+}
+
+class ParameterUndefinedException(field:Any, cause:Throwable=null)
+ extends RuntimeException("Parameter " + field + " undefined.", cause)
+class KnobUndefinedException(field:Any, cause:Throwable=null)
+ extends RuntimeException("Knob " + field + " undefined.", cause)
+
+// Knobs are top level free variables that go into the constraint solver.
+final case class Knob[T](name:Any)
+
+
+class ChiselConfig(
+ val topDefinitions: World.TopDefs = { (a,b,c) => {throw new scala.MatchError(a)}},
+ val topConstraints: List[ViewSym=>Ex[Boolean]] = List( ex => ExLit[Boolean](true) ),
+ val knobValues: Any=>Any = { case x => {throw new scala.MatchError(x)}}
+) {
+ import Implicits._
+ type Constraint = ViewSym=>Ex[Boolean]
+
+ def this(that: ChiselConfig) = this(that.topDefinitions,
+ that.topConstraints,
+ that.knobValues)
+
+ def ++(that: ChiselConfig) = {
+ new ChiselConfig(this.addDefinitions(that.topDefinitions),
+ this.addConstraints(that.topConstraints),
+ this.addKnobValues(that.knobValues))
+ }
+
+ def addDefinitions(that: World.TopDefs): World.TopDefs = {
+ (pname,site,here) => {
+ try this.topDefinitions(pname, site, here)
+ catch {
+ case e: scala.MatchError => that(pname, site, here)
+ }
+ }
+ }
+
+ def addConstraints(that: List[Constraint]):List[Constraint] = {
+ this.topConstraints ++ that
+ }
+
+
+ def addKnobValues(that: Any=>Any): Any=>Any = { case x =>
+ try this.knobValues(x)
+ catch {
+ case e: scala.MatchError => that(x)
+ }
+ }
+
+}
+
+object Dump {
+ val dump = mutable.Set[Tuple2[Any,Any]]()
+ val knobList = mutable.ListBuffer[Any]()
+ def apply[T](key:Any,value:T):T = {addToDump(key,value); value}
+ def apply[T](knob:Knob[T]):Knob[T] = {knobList += knob.name; knob}
+ def addToDump(key:Any,value:Any) = dump += ((key,value))
+ def getDump:String = dump.map(_.toString).reduce(_+"\n"+_) + "\n"
+}
+
+// objects given to the user in mask functions (site,here,up)
+abstract class View {
+ // the list of classes in our current path down the heirarchy
+ def path: List[Class[_]]
+
+ protected val deftSite: View // when views are queried without a specifying a site this is the default
+
+ // use `this` view's behavior to query for a parameters value as if
+ // the original site were `site`
+ def apply[T](pname:Any, site:View):T
+ def sym[T](pname:Any, site:View):Ex[T]
+
+ // query for a parameters value using the default site
+ final def apply[T](pname:Any):T = apply[T](pname, deftSite)
+ final def apply[T](field:Field[T]):T = apply[T](field.asInstanceOf[Any], deftSite)
+
+ final def sym[T](pname:Any):Ex[T] = sym[T](pname, deftSite)
+ final def sym[T](field:Field[T]):Ex[T] = sym[T](field.asInstanceOf[Any], deftSite)
+}
+
+/* Wrap a View to make the application return the symbolic expression,
+ * basically a shorthand to save typing '.sym'
+ * before:
+ * val v: View
+ * v.sym[Int]("x") // returns Ex[_]
+ * now:
+ * val vs = ViewSym(v)
+ * vs[Int]("xs") // Ex[_]
+*/
+final case class ViewSym(view:View) {
+ def apply[T](f:Any):Ex[T] = view.sym[T](f)
+ def apply[T](f:Field[T]):Ex[T] = view.sym[T](f)
+ def apply[T](f:Any, site:View):Ex[T] = view.sym[T](f, site)
+ def apply[T](f:Field[T], site:View):Ex[T] = view.sym[T](f, site)
+}
+
+
+// internal type to represent functions that evaluate parameter values
+abstract class _Lookup {
+ var path:List[Class[_]] = null
+
+ def apply[T](pname:Any, site:View):Ex[T]
+
+ // build a new Lookup that just defers to this one
+ final def push() = {
+ val me = this
+ new _Lookup {
+ this.path = me.path
+ def apply[T](pname:Any, site:View) = me.apply(pname, site)
+ }
+ }
+}
+
+// Internal type used as name in all ExVar[T]'s
+sealed abstract class _Var[T]
+
+// Variables which are 'free' parameters when seen from the top level.
+final case class _VarKnob[T](kname:Any) extends _Var[T] {
+ override def toString = kname.toString
+}
+// Variables whose values are computed by `expr`. The term 'let' comes
+// from the idea of 'let' bindings in functional languages i.e.:
+final case class _VarLet[T](pname:Any,expr:Ex[T]) extends _Var[T] {
+ override def toString = pname.toString + "{" + expr.toString + "}"
+}
+
+
+object World {
+ // An alias for the type of function provided by user to describe parameters that
+ // reach the top level. The return of this function can be either:
+ // Knob(k): this parameter maps to the constraint variable `k`
+ // Ex: this parameter is computed using the expression
+ // Any(thing else): variable takes a literal value
+ type TopDefs = (/*pname:*/Any,/*site:*/View,/*here:*/View) => Any/*Knob[_] | Ex[_] | Any*/
+}
+
+// Worlds collect the variable definitions and constraints seen when building hardware.
+abstract class World(
+ topDefs: World.TopDefs
+ ) {
+
+ val _knobs = new mutable.HashSet[Any]
+ abstract class _View extends View {
+ val look: _Lookup
+ def path = look.path
+
+ def apply[T](pname:Any, site:View):T = {
+ _eval(look(pname, site).asInstanceOf[Ex[T]])
+ }
+ def sym[T](pname:Any, site:View):Ex[T] = {
+ _bindLet[T](pname,look(pname, site).asInstanceOf[Ex[T]])
+ }
+ }
+
+ // evaluate an expression against this world
+ def _eval[T](e:Ex[T]):T = {
+ Ex.eval(e, {
+ case v:_VarKnob[_] => {
+ _knobs += v.kname
+ val e = _knobValue(v.kname)
+ if(Dump.knobList.contains(v.kname)) {Dump.addToDump(v.kname,e);e} else e
+ }
+ case v:_VarLet[_] => _eval(v.expr.asInstanceOf[Ex[T]])
+ })
+ }
+
+ // create a view whose default site is itself
+ def _siteView(look:_Lookup):View = {
+ val _look = look
+ new _View {
+ val look = _look
+ val deftSite = this
+ }
+ }
+
+ // create a View which with a supplied default site
+ def _otherView(look:_Lookup, deftSite:View):View = {
+ val _look = look
+ val _deft = deftSite
+ new _View {
+ val look = _look
+ val deftSite = _deft
+ }
+ }
+
+ // the top level lookup
+ def _topLook():_Lookup = {
+ class TopLookup extends _Lookup {
+ this.path = Nil
+
+ def apply[T](pname:Any, site:View):Ex[T] = {
+ val here = _otherView(this, site)
+ (
+ try topDefs(pname, site, here)
+ catch {
+ case e:scala.MatchError => throw new ParameterUndefinedException(pname, e)
+ }
+ ) match {
+ case k:Knob[T] => ExVar[T](_VarKnob[T](k.name))
+ case ex:Ex[T] => _bindLet[T](pname,ex)
+ case lit => ExLit(lit.asInstanceOf[T])
+ }
+ }
+ }
+ new TopLookup
+ }
+
+ def _bindLet[T](pname:Any,expr:Ex[T]):Ex[T]
+
+ def _constrain(e:Ex[Boolean]):Unit
+
+ def _knobValue(kname:Any):Any
+
+ def getConstraints:String = ""
+
+ def getKnobs:String = ""
+}
+
+// a world responsible for collecting all constraints in the first pass
+class Collector(
+ topDefs: World.TopDefs,
+ knobVal: Any=>Any // maps knob names to default-values
+ )
+ extends World(topDefs) {
+
+ val _constraints = new mutable.HashSet[Ex[Boolean]]
+
+ def knobs():List[Any] = {
+ _knobs.toList
+ }
+
+ def constraints():List[Ex[Boolean]] = {
+ _constraints.toList
+ }
+
+ def _bindLet[T](pname:Any,expr:Ex[T]):Ex[T] = {
+ expr match {
+ case e:ExVar[T] => expr
+ case e:ExLit[T] => expr
+ case _ => ExVar[T](_VarLet[T](pname,expr))
+ }
+ }
+
+ def _constrain(c:Ex[Boolean]) = {
+ _constraints += c // add the constraint
+
+ // Also add all equality constraints for all bound variables in the
+ // constraint expression and do it recursively for all expressions
+ // being bound to.
+ var q = List[Ex[_]](c)
+ while(!q.isEmpty) {
+ val e = q.head // pop an expression
+ q = q.tail
+ // walk over the variables in `e`
+ for(e <- Ex.unfurl(e)) {
+ e match {
+ case ExVar(_VarLet(p,e1)) => {
+ // form the equality constraint
+ val c1 = ExEq[Any](e.asInstanceOf[Ex[Any]], e1.asInstanceOf[Ex[Any]])
+ // recurse into the expression if its never been seen before
+ if(!_constraints.contains(c1)) {
+ _constraints += c1
+ q ::= e1 // push
+ }
+ }
+ case _ => {}
+ }
+ }
+ }
+ }
+
+ def _knobValue(kname:Any) = {
+ try knobVal(kname)
+ catch {
+ case e:scala.MatchError => throw new KnobUndefinedException(kname, e)
+ }
+ }
+
+ override def getConstraints:String = if(constraints.isEmpty) "" else constraints.map("( " + _.toString + " )").reduce(_ +"\n" + _) + "\n"
+
+ override def getKnobs:String = if(knobs.isEmpty) "" else {
+ knobs.map(_.toString).reduce(_ + "\n" + _) + "\n"
+ }
+}
+
+// a world instantianted to a specific mapping of knobs to values
+class Instance(
+ topDefs: World.TopDefs,
+ knobVal: Any=>Any
+ )
+ extends World(topDefs) {
+
+ def _bindLet[T](pname:Any,expr:Ex[T]):Ex[T] = expr
+ def _constrain(e:Ex[Boolean]) = {}
+ def _knobValue(kname:Any) = {
+ try knobVal(kname)
+ catch {
+ case e:scala.MatchError => throw new KnobUndefinedException(kname, e)
+ }
+ }
+}
+
+object Parameters {
+ def root(w:World) = {
+ new Parameters(w, w._topLook())
+ }
+ def empty = Parameters.root(new Collector((a,b,c) => {throw new ParameterUndefinedException(a); a},(a:Any) => {throw new KnobUndefinedException(a); a}))
+
+ // Mask making helpers
+
+ // Lift a regular function into a mask by looking for MatchError's and
+ // interpreting those as calls to up
+ def makeMask(mask:(Any,View,View,View)=>Any) = {
+ (f:Any, site:View, here:View, up:View) => {
+ try mask(f,site,here,up)
+ catch {case e:MatchError => up.sym[Any](f, site)}
+ }
+ }
+
+ // Lift a Map to be a mask.
+ def makeMask(mask:Map[Any,Any]) = {
+ (f:Any, site:View, here:View, up:View) => {
+ mask.get(f) match {
+ case Some(y) => y
+ case None => up.sym[Any](f, site)
+ }
+ }
+ }
+
+ // Lift a PartialFunction to be a mask.
+ def makeMask(mask:PartialFunction[Any,Any]) = {
+ (f:Any, site:View, here:View, up:View) => {
+
+ if(mask.isDefinedAt(f))
+ mask.apply(f)
+ else {
+ up.sym[Any](f, site)
+ }
+ }
+ }
+}
+
+class Field[T]
+
+final class Parameters(
+ private val _world: World,
+ private val _look: _Lookup
+ ) {
+
+ private def _site() = _world._siteView(_look)
+
+ // Create a new Parameters that just defers to this one. This is identical
+ // to doing an `alter` but not overriding any values.
+ def push():Parameters =
+ new Parameters(_world, _look.push())
+
+ // parameter's paths should be immutable but I foresee that not being sufficient
+ // when integrated into the chisel Module factory.
+ def path = _look.path
+ def path_=(x:List[Class[_]]) =
+ _look.path = x
+
+ def apply[T](field:Any):T =
+ _world._eval(_look(field, _site())).asInstanceOf[T]
+
+ def apply[T](field:Field[T]):T =
+ _world._eval(_look(field, _site())).asInstanceOf[T]
+
+ def constrain(gen:ViewSym=>Ex[Boolean]) = {
+ val g = gen(new ViewSym(_site()))
+ if(!_world._eval(g)) ChiselError.error("Constraint failed: " + g.toString)
+ _world._constrain(g)
+ }
+
+ private def _alter(mask:(/*field*/Any,/*site*/View,/*here*/View,/*up*/View)=>Any) = {
+ class KidLookup extends _Lookup {
+ this.path = _look.path
+
+ def apply[T](f:Any, site:View):Ex[T] = {
+ val here = _world._otherView(this, site)
+ val up = _world._otherView(_look, site)
+
+ mask(f, site, here, up) match {
+ case e:Ex[T] => e
+ case lit => ExLit(lit.asInstanceOf[T])
+ }
+ }
+ }
+
+ new Parameters(_world, new KidLookup)
+ }
+
+ def alter(mask:(/*field*/Any,/*site*/View,/*here*/View,/*up*/View)=>Any) =
+ _alter(Parameters.makeMask(mask))
+
+ def alter[T](mask:Map[T,Any]) =
+ _alter(Parameters.makeMask(mask.asInstanceOf[Map[Any,Any]]))
+
+ def alterPartial(mask:PartialFunction[Any,Any]) =
+ _alter(Parameters.makeMask(mask))
+}
+
+
+/*
+ Expression Library
+*/
+abstract class Ex[T] {
+ override def toString = Ex.pretty(this)
+}
+
+case class IntEx (expr:Ex[Int]) {
+ def === (x:IntEx):Ex[Boolean] = (ExEq[Int](expr,x.expr))
+ def + (x:IntEx):Ex[Int] = ExAdd(expr,x.expr)
+ def - (x:IntEx):Ex[Int] = ExSub(expr,x.expr)
+ def * (x:IntEx):Ex[Int] = ExMul(expr,x.expr)
+ def % (x:IntEx):Ex[Int] = ExMod(expr,x.expr)
+ def < (x:IntEx):Ex[Boolean] = ExLt(expr,x.expr)
+ def > (x:IntEx):Ex[Boolean] = ExGt(expr,x.expr)
+ def <= (x:IntEx):Ex[Boolean] = ExLte(expr,x.expr)
+ def >= (x:IntEx):Ex[Boolean] = ExGte(expr,x.expr)
+ def in (x:List[IntEx]):Ex[Boolean] = {
+ val canBound = x.map(_.expr match {
+ case e:ExVar[_] => false
+ case _ => true
+ }).reduce(_ && _)
+ if (canBound) {
+ val max = x.map(i => Ex.eval(i.expr,(x:Any)=>null)).max
+ val min = x.map(i => Ex.eval(i.expr,(x:Any)=>null)).min
+ ExAnd(IntEx(expr) in Range(min,max), IntEx(expr) _in x)
+ } else {
+ IntEx(expr) _in x
+ }
+ }
+ def in (x:Range):Ex[Boolean] = ExAnd(ExGte(expr,ExLit[Int](x.min)),ExLte(expr,ExLit[Int](x.max)))
+ private def _in (x:List[IntEx]):Ex[Boolean] = {
+ if (x.isEmpty) ExLit[Boolean](false) else {
+ ExOr(IntEx(expr) === x.head,IntEx(expr) _in x.tail)
+ }
+ }
+}
+
+case class BoolEx (expr:Ex[Boolean]) {
+ def && (x:BoolEx):Ex[Boolean] = ExAnd(expr,x.expr)
+ def || (x:BoolEx):Ex[Boolean] = ExOr(expr,x.expr)
+ def ^ (x:BoolEx):Ex[Boolean] = ExXor(expr,x.expr)
+ def === (x:BoolEx):Ex[Boolean] = ExEq[Boolean](expr,x.expr)
+ def !== (x:BoolEx):Ex[Boolean] = ExEq[Boolean](expr,x.expr)
+}
+
+object Implicits {
+ implicit def ExInt_IntEx(i:Ex[Int]):IntEx = IntEx(i)
+ implicit def Int_IntEx(i:Int):IntEx = IntEx(ExLit[Int](i))
+ implicit def ExBool_BoolEx(b:Ex[Boolean]):BoolEx = BoolEx(b)
+ implicit def Bool_IntEx(b:Boolean):BoolEx = BoolEx(ExLit[Boolean](b))
+
+ implicit def ListInt_ListExInt(l:List[Int]):List[IntEx] = l.map((x:Int) => IntEx(ExLit[Int](x)))
+ implicit def ListExInt_ListExInt(l:List[Ex[Int]]):List[IntEx] = l.map((x:Ex[Int]) => IntEx(x))
+}
+
+final case class ExLit[T](value:T) extends Ex[T]
+final case class ExVar[T](name:Any) extends Ex[T]
+
+final case class ExAnd(a:Ex[Boolean], b:Ex[Boolean]) extends Ex[Boolean]
+final case class ExOr(a:Ex[Boolean], b:Ex[Boolean]) extends Ex[Boolean]
+final case class ExXor(a:Ex[Boolean], b:Ex[Boolean]) extends Ex[Boolean]
+
+final case class ExEq[T](a:Ex[T], b:Ex[T]) extends Ex[Boolean]
+final case class ExNeq[T](a:Ex[T], b:Ex[T]) extends Ex[Boolean]
+
+final case class ExLt(a:Ex[Int], b:Ex[Int]) extends Ex[Boolean]
+final case class ExLte(a:Ex[Int], b:Ex[Int]) extends Ex[Boolean]
+final case class ExGt(a:Ex[Int], b:Ex[Int]) extends Ex[Boolean]
+final case class ExGte(a:Ex[Int], b:Ex[Int]) extends Ex[Boolean]
+final case class ExAdd(a:Ex[Int], b:Ex[Int]) extends Ex[Int]
+final case class ExSub(a:Ex[Int], b:Ex[Int]) extends Ex[Int]
+final case class ExMul(a:Ex[Int], b:Ex[Int]) extends Ex[Int]
+final case class ExMod(a:Ex[Int], b:Ex[Int]) extends Ex[Int]
+
+object Ex {
+ // evaluate an expression given a context that maps variable names to values
+ def eval[T](e:Ex[T], ctx:Any=>Any):T = e match {
+ case ExLit(v) => v.asInstanceOf[T]
+ case ExVar(nm) => ctx(nm).asInstanceOf[T]
+ case ExAnd(a,b) => eval(a,ctx) && eval(b,ctx)
+ case ExOr(a,b) => eval(a,ctx) || eval(b,ctx)
+ case ExXor(a,b) => eval(a,ctx) ^ eval(b,ctx)
+ case e:ExEq[u] => eval(e.a,ctx) == eval(e.b,ctx)
+ case e:ExNeq[u] => eval(e.a,ctx) != eval(e.b,ctx)
+ case ExLt(a,b) => eval(a,ctx) < eval(b,ctx)
+ case ExLte(a,b) => eval(a,ctx) <= eval(b,ctx)
+ case ExGt(a,b) => eval(a,ctx) > eval(b,ctx)
+ case ExGte(a,b) => eval(a,ctx) >= eval(b,ctx)
+ case ExAdd(a,b) => eval(a,ctx) + eval(b,ctx)
+ case ExSub(a,b) => eval(a,ctx) - eval(b,ctx)
+ case ExMul(a,b) => eval(a,ctx) * eval(b,ctx)
+ case ExMod(a,b) => eval(a,ctx) % eval(b,ctx)
+ }
+
+ // get shallow list of subexpressions
+ def subExs(e:Ex[_]):List[Ex[_]] = e match {
+ case ExLit(_) => Nil
+ case ExVar(_) => Nil
+ case ExAnd(a,b) => List(a,b)
+ case ExOr(a,b) => List(a,b)
+ case ExXor(a,b) => List(a,b)
+ case ExEq(a,b) => List(a,b)
+ case ExNeq(a,b) => List(a,b)
+ case ExLt(a,b) => List(a,b)
+ case ExLte(a,b) => List(a,b)
+ case ExGt(a,b) => List(a,b)
+ case ExGte(a,b) => List(a,b)
+ case ExAdd(a,b) => List(a,b)
+ case ExSub(a,b) => List(a,b)
+ case ExMul(a,b) => List(a,b)
+ case ExMod(a,b) => List(a,b)
+ }
+
+ // get all subexpressions including the expression given
+ def unfurl(e:Ex[_]):List[Ex[_]] =
+ e :: (subExs(e) flatMap unfurl)
+
+ // pretty-print expression
+ def pretty(e:Ex[_]):String = {
+ // precedence rank for deciding where to put parentheses
+ def rank(e:Ex[_]):Int = e match {
+ case e:ExAnd => 40
+ case e:ExOr => 50
+ case e:ExXor => 50
+ case e:ExEq[_] => 30
+ case e:ExNeq[_] => 30
+ case e:ExLt => 30
+ case e:ExLte => 30
+ case e:ExGt => 30
+ case e:ExGte => 30
+ case e:ExAdd => 20
+ case e:ExSub => 20
+ case e:ExMul => 20
+ case e:ExMod => 20
+ case e:ExLit[_] => 0
+ case e:ExVar[_] => 0
+ }
+
+ val r = rank(e)
+
+ def term(t:Ex[_]):String = {
+ val rt = rank(t)
+ //if(rt >= r)
+ "( " + t.toString + " )"
+ //else
+ //t.toString
+ }
+
+ import Implicits._
+ e match {
+ case ExLit(v) => v.toString
+ case e:ExVar[_]=> "$"+e.name
+ case ExAnd(a,b) => term(a)+" && "+term(b)
+ case ExOr(a,b) => term(a)+" || "+term(b)
+ case ExXor(a,b) => term(a)+" ^ "+term(b)
+ case ExEq(a,b) => term(a)+" = "+term(b)
+ case ExNeq(a,b) => term(a)+" != "+term(b)
+ case ExLt(a,b) => term(a)+" < "+term(b)
+ case ExLte(a,b) => term(a)+" <= "+term(b)
+ case ExGt(a,b) => term(a)+" > "+term(b)
+ case ExGte(a,b) => term(a)+" >= "+term(b)
+ case ExAdd(a,b) => term(a)+" + "+term(b)
+ case ExSub(a,b) => term(a)+" - "+term(b)
+ case ExMul(a,b) => term(a)+" * "+term(b)
+ case ExMod(a,b) => term(a)+" % "+term(b)
+ }
+ }
+}