├── .sbtopts ├── LICENSE ├── README.md ├── build.sbt └── src ├── main └── scala │ └── scalogno │ ├── core.scala │ ├── deriv.scala │ ├── engine.scala │ ├── meta.scala │ └── tabling.scala └── test └── scala └── scalogno ├── core.scala ├── deriv.scala └── tabling.scala /.sbtopts: -------------------------------------------------------------------------------- 1 | -J-Xms6G 2 | -J-Xmx6G 3 | -J-Xss10M 4 | -J-XX:+UseParallelGC 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Nada Amin, William E Byrd, Tiark Rompf 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | scalogno 2 | ======== 3 | 4 | Scalogno as presented in APLAS'19 ([PDF](https://namin.seas.harvard.edu/files/namin/files/scalogno.pdf)). 5 | 6 | - [2.1 Relations as Functions](src/test/scala/scalogno/core.scala#L274) 7 | - [2.1 edge example](src/test/scala/scalogno/core.scala#L281) 8 | - [2.1 path def example](src/main/scala/scalogno/core.scala#L189) 9 | - [2.1 path example](src/test/scala/scalogno/core.scala#L289) 10 | - [2.2 Higher-Order Relations as Higher-Order Functions](src/test/scala/scalogno/core.scala#L299) 11 | - [2.2 reflexive transitive closure](src/test/scala/scalogno/core.scala#L318) 12 | - [2.3 Object-Oriented Encapsulation](src/main/scala/scalogno/core.scala#L186) 13 | - [2.3 example](src/test/scala/scalogno/core.scala#L272) 14 | - [3.1 Designing Logic Engines for Meta-Programming (Fig 1)](src/main/scala/scalogno/engine.scala) 15 | - [3.3 path rule def example](src/test/scala/scalogno/core.scala#L336) 16 | - [3.3 path example](src/test/scala/scalogno/core.scala#L347) 17 | - [3.4 Clause Reification as Controlled Side Effect](src/main/scala/scalogno/meta.scala#L62) 18 | 19 | - [4 fib example](src/test/scala/scalogno/tabling.scala#L91) 20 | - [4.2 Memoization with Symbolic State Transitions](src/main/scala/scalogno/tabling.scala) 21 | - [4.3 Example: Tabled Graph Evaluation](src/test/scala/scalogno/tabling.scala#L133) 22 | - [4.3 example](src/test/scala/scalogno/tabling.scala#L192) 23 | - [4.4 Definite Clause Grammar (DFG)](src/test/scala/scalogno/tabling.scala#L6) 24 | - [4.4 example](src/test/scala/scalogno/tabling.scala#L28) 25 | 26 | Beyond APLAS: 27 | 28 | - the [all](https://github.com/namin/scalogno/tree/all) branch adds an SMT solver as a backend, and combines it with tabling too. Work remains to refactor the combination into composable modules, and ... find a killer app beyond shortest paths. :) 29 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | name := "scalogno" 2 | 3 | version := "0.4" 4 | 5 | scalaVersion := "2.13.8" 6 | 7 | libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.11" % "test" 8 | 9 | parallelExecution in Test := false 10 | -------------------------------------------------------------------------------- /src/main/scala/scalogno/core.scala: -------------------------------------------------------------------------------- 1 | package scalogno 2 | 3 | import scala.language.implicitConversions 4 | 5 | trait InjectBase extends Base { 6 | 7 | trait Inject[T] { 8 | def toTerm(x:T): Exp[T] 9 | } 10 | 11 | implicit def inject[T:Inject](x:T) = implicitly[Inject[T]].toTerm(x) 12 | 13 | implicit object InjectString extends Inject[String] { 14 | def toTerm(s: String): Exp[String] = term(s,Nil) 15 | } 16 | 17 | } 18 | 19 | 20 | trait Ordering extends Base { 21 | 22 | trait Ord[T] { 23 | def lt(x:Exp[T],y:Exp[T]): Rel 24 | } 25 | 26 | implicit class OrdOps[T:Ord](x:Exp[T]) { 27 | def <(y:Exp[T]): Rel = implicitly[Ord[T]].lt(x,y) 28 | } 29 | 30 | } 31 | 32 | 33 | 34 | trait NatBase extends InjectBase with Ordering { 35 | implicit val injectNat = new Inject[Int] { 36 | def toTerm(x: Int): Exp[Int] = nat(x) 37 | } 38 | implicit val ordNat = new Ord[Int] { 39 | def lt(x:Exp[Int],y:Exp[Int]): Rel = lessThan(x,y) 40 | } 41 | 42 | def nat(x: Int): Exp[Int] = if (x == 0) zero else succ(x-1) 43 | 44 | def succ(n: Exp[Int]): Exp[Int] = term("s",List(n)) 45 | def zero: Exp[Int] = term("z",List()) 46 | 47 | def lessThan(a: Exp[Int], b: Exp[Int]): Rel = 48 | exists[Int] { b1 => b === succ(b1) && { a === zero || exists[Int] { a1 => 49 | (a === succ(a1)) && lessThan(a1,b1) 50 | }}} 51 | 52 | def add(a: Exp[Int], b: Exp[Int], c: Exp[Int]): Rel = 53 | (a === zero) && (b === c) || 54 | exists[Int,Int] { (a1,c1) => 55 | (a === succ(a1)) && (c === succ(c1)) && add(a1,b,c1) 56 | } 57 | 58 | } 59 | 60 | 61 | 62 | 63 | trait ListBase extends InjectBase with NatBase with Ordering { 64 | implicit def injectPair[T:Inject,U:Inject] = new Inject[(T,U)] { 65 | def toTerm(x: (T,U)): Exp[(T,U)] = pair(x._1,x._2) 66 | } 67 | implicit def injectList[T:Inject] = new Inject[List[T]] { 68 | def toTerm(x: List[T]): Exp[List[T]] = list(x:_*) 69 | } 70 | implicit def ordList[T:Ord] = new Ord[List[T]] { 71 | def lt(x:Exp[List[T]],y:Exp[List[T]]): Rel = lessLex[T]((a1,a2) => a1 < a2, x,y) 72 | } 73 | 74 | implicit def injectPairShallow[T,U](x: (Exp[T],Exp[U])) = pair(x._1,x._2) 75 | implicit def injectListShallow[T](xs: List[Exp[T]]): Exp[List[T]] = if (xs.isEmpty) nil else cons(xs.head,xs.tail) 76 | 77 | 78 | def list[T:Inject](xs: T*): Exp[List[T]] = if (xs.isEmpty) nil else cons(inject(xs.head),list(xs.tail:_*)) 79 | 80 | def cons[T](hd: Exp[T], tl: Exp[List[T]]): Exp[List[T]] = term("cons",List(hd,tl)) 81 | def nil: Exp[List[Nothing]] = term("nil",List()) 82 | def pair[A,B](a: Exp[A], b: Exp[B]): Exp[(A,B)] = term("pair",List(a,b)) 83 | 84 | object Cons { 85 | def unapply[T](x: Exp[List[T]]): Some[(Exp[T],Exp[List[T]])] = { 86 | val h = fresh[T] 87 | val t = fresh[List[T]] 88 | x === cons(h,t) 89 | Some((h,t)) 90 | } 91 | } 92 | object Pair { 93 | def unapply[A,B](x: Exp[(A,B)]): Some[(Exp[A],Exp[B])] = { 94 | val a = fresh[A] 95 | val b = fresh[B] 96 | x === pair(a,b) 97 | Some((a,b)) 98 | } 99 | } 100 | 101 | 102 | def contains[T](xs: Exp[List[T]], y:Exp[T]): Rel = 103 | exists[T,List[T]] { (x,xs1) => 104 | xs === cons(x,xs1) && (x === y || contains(xs1,y)) 105 | } 106 | 107 | def append[T](as: Exp[List[T]], bs: Exp[List[T]], cs: Exp[List[T]]): Rel = 108 | (as === nil && bs === cs) || 109 | exists[T,List[T],List[T]] { (h,t1,t2) => 110 | (as === cons(h,t1)) && (cs === cons(h,t2)) && append(t1,bs,t2) 111 | } 112 | 113 | def map[T,U](f: (Exp[T],Exp[U]) => Rel, as: Exp[List[T]], bs: Exp[List[U]]): Rel = 114 | (as === nil) && (bs === nil) || 115 | exists[T,U,List[T],List[U]] { (a,b,as1,bs1) => 116 | (as === cons(a,as1)) && f(a,b) && (bs === cons(b,bs1)) && map[T,U](f,as1,bs1) 117 | } 118 | 119 | def mapf[T,U](as: Exp[List[T]], f: (Exp[T],Exp[U]) => Rel): Exp[List[U]] = { 120 | val bs = fresh[List[U]] 121 | map(f,as,bs) // needs delayed mode 122 | bs 123 | } 124 | 125 | def flatMap[T,U](f: (Exp[T],Exp[List[U]]) => Rel, as: Exp[List[T]], cs: Exp[List[U]]): Rel = 126 | (as === nil) && (cs === nil) || 127 | exists[T,List[U],List[T],List[U]] { (a,bs,as1,cs1) => 128 | (as === cons(a,as1)) && f(a,bs) && append(bs, cs1, cs) && flatMap[T,U](f,as1,cs1) 129 | } 130 | 131 | 132 | def lessLex[T](f: (Exp[T],Exp[T]) => Rel, as: Exp[List[T]], bs: Exp[List[T]]): Rel = 133 | exists[T,List[T]] { (b,bs1) => (bs === cons(b,bs1)) && { (as === nil) || 134 | exists[T,List[T]] { (a,as1) => 135 | (as === cons(a,as1)) && { f(a,b) || (a === b) && lessLex[T](f,as1,bs1) } 136 | } 137 | }} 138 | } 139 | 140 | trait TreeBase extends InjectBase with NatBase with Ordering { 141 | 142 | trait Tree[+T,-U] 143 | 144 | implicit def injectPair[T:Inject,U:Inject](x: (T,U)) = (inject(x._1), inject(x._2)) 145 | 146 | def tree[T,U](xs: (Exp[T],Exp[U])*): Exp[Tree[T,U]] = if (xs.length == 0) empty else { 147 | val n = xs.length/2 148 | val (k,v) = xs(n) 149 | branch(tree(xs.slice(0,n):_*),k,v,tree(xs.slice(n+1,xs.length):_*)) 150 | } 151 | 152 | def branch[T,U](l: Exp[Tree[T,U]], k: Exp[T], v: Exp[U], r: Exp[Tree[T,U]]): Exp[Tree[T,U]] = term("branch",List(l,k,v,r)) 153 | def leaf[T,U](k:Exp[T],v: Exp[U]) = branch(empty,k,v,empty) 154 | def empty: Exp[Tree[Nothing,Any]] = term("nil",List()) 155 | 156 | def lookupAll[T,U](as: Exp[Tree[T,U]], k: Exp[T], v: Exp[U]): Rel = 157 | exists[Tree[T,U],Tree[T,U],T,U] { (l,r,k1,v1) => 158 | as === branch(l,k1,v1,r) && { 159 | (k === k1 && v === v1) || 160 | lookupAll(l,k,v) || lookupAll(r,k,v) 161 | } 162 | } 163 | 164 | def lookup[T:Ord,U](as: Exp[Tree[T,U]], k: Exp[T], v: Exp[U]): Rel = 165 | exists[Tree[T,U],Tree[T,U],T,U] { (l,r,k1,v1) => 166 | as === branch(l,k1,v1,r) && { 167 | (k === k1 && v === v1) || 168 | (k < k1 && lookup(l,k,v)) || 169 | (k1 < k && lookup(r,k,v)) 170 | } 171 | } 172 | 173 | def lookupLess[T:Ord,U](as: Exp[Tree[T,U]], k: Exp[T], v: Exp[U]): Rel = 174 | exists[Tree[T,U],Tree[T,U],T,U] { (l,r,k1,v1) => 175 | as === branch(l,k1,v1,r) && { 176 | (lookupLess(l,k,v)) || 177 | (k1 < k && v === v1) || 178 | (k1 < k && lookupLess(r,k,v)) 179 | } 180 | } 181 | 182 | } 183 | 184 | trait GraphBase extends InjectBase with NatBase { 185 | 186 | // APLAS 2.3 Object-Oriented Encapsulation 187 | trait Graph[T] { 188 | def edge(a: Exp[T], b: Exp[T]): Rel 189 | // APLAS 2.1 path def example 190 | def path(a: Exp[T], b: Exp[T]): Rel = 191 | edge(a,b) || exists[T] { z => edge(a,z) && path(z,b) } 192 | } 193 | 194 | } 195 | 196 | trait MetaGraphBase extends GraphBase with ListBase with Engine with MetaVanilla with MetaReifyVanilla with MetaTracer { 197 | 198 | /* 199 | (define (patho-clause head tail) 200 | (fresh (x y) 201 | (== head ‘(patho ,x ,y)) 202 | (conde 203 | ((edgeo x y) (== tail ’())) 204 | ((fresh (z) 205 | (edgeo x z) 206 | (== tail ‘((patho ,z ,y)))))))) 207 | */ 208 | 209 | def pathTerm[T](a: Exp[T], b: Exp[T]) = term[Goal]("path",List(a,b)) 210 | 211 | def pathClause1[T](g: Graph[T])(head: Exp[Goal], body: Exp[List[Goal]]) = { 212 | exists[T,T] { (a,b) => 213 | (head === pathTerm(a,b)) && { 214 | (g.edge(a,b) && (body === nil)) || 215 | exists[T] { z => 216 | g.edge(a,z) && (body === cons(pathTerm(z,b),nil)) 217 | } 218 | } 219 | } 220 | } 221 | 222 | def edgeTerm[T](a: Exp[T], b: Exp[T]) = term[Goal]("edge",List(a,b)) 223 | 224 | def pathFullClause1[T](g: Graph[T])(head: Exp[Goal], body: Exp[List[Goal]]) = { 225 | exists[T,T] { (a,b) => 226 | ((head === pathTerm(a,b)) && ( 227 | (body === cons(edgeTerm(a,b),nil)) || 228 | exists[T] { z => 229 | body === cons(edgeTerm(a,z), cons(pathTerm(z,b),nil)) 230 | } 231 | )) || 232 | ((head === edgeTerm(a,b)) && g.edge(a,b)) 233 | } 234 | } 235 | 236 | /* 237 | (run 10 (q) 238 | ((vanilla* patho-clause) 239 | ‘((patho a ,q)))) => (b c a b c a b c a b) 240 | */ 241 | 242 | def pathClause2[T](g: Graph[T])(a: Exp[T], b: Exp[T]) = { (head: Exp[Goal], body: Exp[List[Goal]]) => 243 | (head === pathTerm(a,b)) && { 244 | g.edge(a,b) && (body === nil) || 245 | exists[T] { z => 246 | g.edge(a,z) && (body === cons(pathTerm(z,b),nil)) 247 | } 248 | } 249 | } 250 | 251 | def path2[T](g: Graph[T]): (Exp[T],Exp[T])=> Rel = 252 | rule("path"/*+g.toString*/) { (a,b) => 253 | g.edge(a,b) || 254 | exists[T] { z => 255 | g.edge(a,z) && path2(g)(z,b) 256 | } 257 | } 258 | } 259 | 260 | 261 | trait ReifyUtilsBase extends Base with InjectBase with ListBase with Engine { 262 | def rule[T,U](s: String)(f: (Exp[T],Exp[U]) => Rel): (Exp[T],Exp[U]) => Rel 263 | def globalTrace: () => Exp[List[List[String]]] 264 | } 265 | 266 | trait ReifyUtilsDynVars extends ReifyUtilsBase with InjectBase with ListBase with Engine { 267 | val _globalTrace = DVar(nil: Exp[List[List[String]]]) 268 | override def globalTrace = () => _globalTrace() 269 | 270 | def rule[T,U](s: String)(f: (Exp[T],Exp[U]) => Rel): (Exp[T],Exp[U]) => Rel = 271 | { (a,b) => 272 | _globalTrace := cons(term(s,List(a,b)), _globalTrace()) 273 | f(a,b) 274 | } 275 | } 276 | 277 | trait ReifyUtils extends ReifyUtilsBase with InjectBase with ListBase with Engine { 278 | 279 | var globalTrace0: Exp[List[List[String]]] = nil 280 | 281 | def globalTrace = () => globalTrace0 282 | def globalTrace_=(x:Exp[List[List[String]]]) = globalTrace0 = x 283 | 284 | // APLAS 3.3 Tracing With Dynamic Variables 285 | def rule[T,U](s: String)(f: (Exp[T],Exp[U]) => Rel): (Exp[T],Exp[U]) => Rel = 286 | { (a,b) => 287 | globalTrace = cons(term(s,List(a,b)),globalTrace()); 288 | f(a,b) 289 | } 290 | } 291 | 292 | 293 | 294 | trait STLC extends Base with InjectBase with ListBase with Engine { 295 | 296 | trait LTerm 297 | trait LType 298 | trait Deriv 299 | trait Rule 300 | 301 | type Sym 302 | type Env 303 | 304 | implicit class EnvOps(x: Exp[Env]) { 305 | def |- (y: (Exp[LTerm],Exp[LType])) = term[Deriv]("|-", List(x,y._1,y._2)) 306 | } 307 | 308 | implicit class TypeOps(x: Exp[LType]) { 309 | def :: (e: Exp[LTerm]) = (e,x) 310 | def -> (y: Exp[LType]) = term[LType]("->", List(x,y)) 311 | } 312 | 313 | implicit class TermOps(x: Exp[LTerm]) { 314 | def app(y: Exp[LTerm]) = term[LTerm]("@", List(x,y)) 315 | } 316 | 317 | def sym(x: Exp[Sym]) = term[LTerm]("var", List(x)) 318 | 319 | def lam(x: Exp[Sym], y: Exp[LTerm]) = term[LTerm]("lam", List(x,y)) 320 | 321 | // judgments 322 | 323 | def extend(g: Exp[Env], x: Exp[Sym], tp: Exp[LType], g1: Exp[Env]): Rel 324 | 325 | def lookup(g: Exp[Env], x: Exp[Sym], tp: Exp[LType]): Rel 326 | 327 | def typecheck(d: Exp[Deriv]): Rel = 328 | exists[Env,Sym,LType] { (G,x,t1) => 329 | val a = G |- sym(x) :: t1 330 | 331 | d === a && lookup(G,x,t1) 332 | } || 333 | exists[Env,Env,Sym,LTerm,LType,LType] { (G,G1,x,e,t1,t2) => 334 | val a = G |- lam(x,e) :: (t1 -> t2) 335 | val b = G1 |- e :: t2 336 | 337 | d === a && extend(G,x,t1,G1) && typecheck(b) 338 | } || 339 | exists[Env,LTerm,LTerm,LType,LType] { (G,e1,e2,t1,t2) => 340 | val a = G |- (e1 app e2) :: t2 341 | val b = G |- e1 :: (t1 -> t2) 342 | val c = G |- e2 :: t1 343 | 344 | d === a && typecheck(b) && typecheck(c) 345 | } 346 | } 347 | 348 | trait STLC_ReverseDeBruijn extends STLC { 349 | 350 | // env is list of types, indexed by int 351 | 352 | type Sym = Int 353 | type Env = List[LType] 354 | 355 | def extend(g: Exp[Env], x: Exp[Sym], tp: Exp[LType], g1: Exp[Env]): Rel = 356 | g1 === cons(tp,g) && freein(g,x) 357 | 358 | 359 | def lookup(g: Exp[Env], x: Exp[Sym], tp: Exp[LType]): Rel = 360 | exists[LType,Env] { (hd,tl) => 361 | g === cons(hd,tl) && { 362 | freein(tl,x) && (hd === tp) || 363 | lookup(tl,x,tp) 364 | } 365 | } 366 | 367 | def freein(g: Exp[Env], x: Exp[Sym]): Rel = 368 | g === nil && x === zero || 369 | exists[Sym,Env] { (x1,tl) => 370 | g === cons(fresh[LType],tl) && x === succ(x1) && freein(tl,x1) 371 | } 372 | } 373 | 374 | trait STLC_Nat extends STLC { 375 | 376 | implicit class NatTermOps(x: Exp[LTerm]) { 377 | def +(y: Exp[LTerm]) = term[LTerm]("+", List(x,y)) 378 | } 379 | 380 | def tnat = term[LType]("nat",Nil) 381 | 382 | def cnat(x: Exp[Int]) = term[LTerm]("nat",List(x)) 383 | 384 | override def typecheck(d: Exp[Deriv]): Rel = 385 | exists[Env,Int] { (G,x) => 386 | val a = G |- cnat(x) :: tnat 387 | 388 | d === a 389 | } || 390 | exists[Env,LTerm,LTerm] { (G,e1,e2) => 391 | val a = G |- (e1 + e2) :: tnat 392 | val b = G |- e1 :: tnat 393 | val c = G |- e2 :: tnat 394 | 395 | d === a && typecheck(b) && typecheck(c) 396 | } || 397 | super.typecheck(d) 398 | 399 | } 400 | -------------------------------------------------------------------------------- /src/main/scala/scalogno/deriv.scala: -------------------------------------------------------------------------------- 1 | package scalogno 2 | 3 | // TODO: in progress 4 | // want to achieve something like 5 | // https://github.com/namin/TAPL-in-miniKanren-cKanren-core.logic/blob/master/clojure-tapl/tapl/src/tapl/stlc_deriv.clj#L67 6 | trait Deriv extends MetaGraphBase { 7 | def deriv(clause: Clause)(tree: Exp[List[List[Goal]]])(goals: Exp[List[Goal]]): Rel = { 8 | ((goals === nil) && (tree === nil)) || 9 | exists[Goal,List[Goal],List[Goal],List[List[Goal]],List[List[Goal]]] { (g,gs,b,tb,ts) => 10 | (goals === cons(g, gs)) && 11 | clause(g,b) && 12 | tree === cons(cons(g, cons("<--", cons(tb, nil))), ts) && 13 | deriv(clause)(tb)(b) && 14 | deriv(clause)(ts)(gs) 15 | } 16 | } 17 | } 18 | 19 | trait MetaSTLC extends STLC with MetaGraphBase { 20 | override def typecheck(d: Exp[Deriv]): Rel = 21 | rule1("tc")(super.typecheck)(d) 22 | } 23 | -------------------------------------------------------------------------------- /src/main/scala/scalogno/engine.scala: -------------------------------------------------------------------------------- 1 | package scalogno 2 | 3 | import scala.collection._ 4 | 5 | trait Base { 6 | val Backtrack = new Exception 7 | 8 | var varCount: Int = 0 9 | def freshId = { 10 | var id = varCount 11 | varCount += 1 12 | id 13 | } 14 | 15 | implicit class RelOps(a: => Rel) { 16 | def &&(b: => Rel) = infix_&&(a,b) 17 | def ||(b: => Rel) = infix_||(a,b) 18 | } 19 | implicit class ExpOps[T](a: Exp[T]) { 20 | def ===[U](b: Exp[U]) = infix_===(a,b) 21 | } 22 | 23 | def exists[T,U](f: (Exp[T],Exp[U]) => Rel): Rel = { 24 | f(fresh[T],fresh[U]) 25 | } 26 | def exists[T,U,V](f: (Exp[T],Exp[U],Exp[V]) => Rel): Rel = { 27 | f(fresh[T],fresh[U],fresh[V]) 28 | } 29 | def exists[T,U,V,W](f: (Exp[T],Exp[U],Exp[V],Exp[W]) => Rel): Rel = { 30 | f(fresh[T],fresh[U],fresh[V],fresh[W]) 31 | } 32 | def exists[T,U,V,W,X](f: (Exp[T],Exp[U],Exp[V],Exp[W],Exp[X]) => Rel): Rel = { 33 | f(fresh[T],fresh[U],fresh[V],fresh[W],fresh[X]) 34 | } 35 | def exists[T,U,V,W,X,Y](f: (Exp[T],Exp[U],Exp[V],Exp[W],Exp[X],Exp[Y]) => Rel): Rel = { 36 | f(fresh[T],fresh[U],fresh[V],fresh[W],fresh[X],fresh[Y]) 37 | } 38 | def exists[T,U,V,W,X,Y,Z](f: (Exp[T],Exp[U],Exp[V],Exp[W],Exp[X],Exp[Y],Exp[Z]) => Rel): Rel = { 39 | f(fresh[T],fresh[U],fresh[V],fresh[W],fresh[X],fresh[Y],fresh[Z]) 40 | } 41 | 42 | // dynamically scoped variables 43 | var dvars: immutable.Map[Int, Any] = immutable.Map.empty 44 | case class DVar[T](val id: Int, val default: T) extends (() => T) { 45 | dvar_set(id,default) 46 | def apply() = dvar_get[T](id) 47 | def :=(v: T) = dvar_set[T](id,v) 48 | } 49 | var dvarCount = 1 50 | def DVar[T](v: T): DVar[T] = { 51 | val id = dvarCount 52 | dvarCount += 1 53 | new DVar[T](id, v) 54 | } 55 | def dvar_set[T](id: Int, v: T): Unit = dvars += id -> v 56 | def dvar_get[T](id: Int): T = dvars(id).asInstanceOf[T] 57 | def dvar_upd[T](id: Int)(f: T => T): Unit = dvars += id -> f(dvar_get(id)) 58 | 59 | // goals and relations 60 | trait Rel { def exec(call: Exec)(k: Cont): Unit } 61 | type Exec = GoalThunk => Cont => Unit 62 | type Cont = () => Unit 63 | type GoalThunk = () => Rel 64 | 65 | // unconditional success 66 | val Yes = new Rel { 67 | def exec(call: Exec)(k: Cont) = k() } 68 | 69 | // unconditional failure 70 | val No = new Rel { 71 | def exec(call: Exec)(k: Cont) = throw Backtrack } 72 | 73 | def infix_&&(a: => Rel, b: => Rel): Rel = new Rel { 74 | def exec(call: Exec)(k: Cont) = 75 | call(() => a) { () => call(() => b)(k) } } 76 | 77 | def infix_||(a: => Rel, b: => Rel): Rel = new Rel { 78 | def exec(call: Exec)(k: Cont) = { 79 | call(() => a)(k); call(() => b)(k) }} 80 | 81 | // logic terms 82 | case class Exp[+T](id: Int) 83 | def fresh[T] = Exp(freshId) 84 | 85 | def exists[T](f: Exp[T] => Rel): Rel = f(fresh) 86 | 87 | def infix_===[T](a: => Exp[T], b: => Exp[T]): Rel = { 88 | register(IsEqual(a,b)); Yes } 89 | 90 | def term[T](key: String, args: List[Exp[Any]]): Exp[T] = { 91 | val e = fresh[T]; register(IsTerm(e.id, key, args)); e } 92 | 93 | // constraints 94 | abstract class Constraint 95 | case class IsTerm(id: Int, key: String, args: List[Exp[Any]]) 96 | extends Constraint 97 | case class IsEqual(x: Exp[Any], y: Exp[Any]) 98 | extends Constraint 99 | 100 | var cstore: immutable.Set[Constraint] = immutable.Set.empty 101 | def conflict(cs: Set[Constraint], c: Constraint): Boolean = { 102 | def prop(c1: Constraint, c2: Constraint)(fail: () => Nothing): List[Constraint] = (c1,c2) match { 103 | case (IsEqual(a1,b1), IsEqual(a2,b2)) if a1 == a2 || a1 == b2 || b1 == a2 || b1 == b2 => 104 | List(IsEqual(a1,a2),IsEqual(a1,b2),IsEqual(b1,a2),IsEqual(b1,b2)) 105 | 106 | case (IsEqual(Exp(a),Exp(b)), IsTerm(a1, key, args)) if a == a1 => 107 | List(IsTerm(b, key, args)) 108 | case (IsTerm(a1, key, args), IsEqual(Exp(a),Exp(b))) if a == a1 => 109 | List(IsTerm(b, key, args)) 110 | case (IsTerm(b1, key, args), IsEqual(Exp(a),Exp(b))) if b == b1 => 111 | List(IsTerm(a, key, args)) 112 | case (IsEqual(Exp(a),Exp(b)), IsTerm(b1, key, args)) if b == b1 => 113 | List(IsTerm(a, key, args)) 114 | 115 | case (IsTerm(a1, key1, args1), IsTerm(a2, key2, args2)) if a1 == a2 => 116 | if (key1 != key2 || args1.length != args2.length) fail() 117 | (args1,args2).zipped map (IsEqual(_,_)) 118 | case _ => Nil 119 | } 120 | 121 | val fail = () => throw Backtrack 122 | 123 | val cn = cs flatMap { c2 => prop(c, c2)(fail) } 124 | cstore += c 125 | cn foreach register 126 | false 127 | } 128 | 129 | def register(c: Constraint): Unit = { 130 | if (cstore.contains(c)) return 131 | if (conflict(cstore,c)) throw Backtrack 132 | } 133 | } 134 | 135 | trait Engine extends Base { 136 | // execution (depth-first) 137 | def run[T](f: Exp[T] => Rel): Seq[String] = { 138 | def call(e: () => Rel)(k: Cont): Unit = { 139 | val cstore1 = cstore 140 | val dvars1 = dvars 141 | try { 142 | e().exec(call)(k) 143 | } catch { 144 | case Backtrack => // OK 145 | } finally { 146 | cstore = cstore1 147 | dvars = dvars1 148 | } 149 | } 150 | val q = fresh[T] 151 | val res = mutable.ListBuffer[String]() 152 | call(() => f(q)) { () => 153 | res += extractStr(q) 154 | } 155 | res.toList 156 | } 157 | 158 | def runN[T](max: Int)(f: Exp[T] => Rel): Seq[String] = { 159 | def call(e: () => Rel)(k: Cont): Unit = { 160 | val cstore1 = cstore 161 | val dvars1 = dvars 162 | try { 163 | e().exec(call)(k) 164 | } catch { 165 | case Backtrack => // OK 166 | } finally { 167 | cstore = cstore1 168 | dvars = dvars1 169 | } 170 | } 171 | val q = fresh[T] 172 | val res = mutable.ListBuffer[String]() 173 | val Done = new Exception 174 | try { 175 | call(() => f(q)) { () => 176 | res += extractStr(q) 177 | if (res.length>=max) throw Done 178 | } 179 | } catch { case Done => } 180 | res.toList 181 | } 182 | 183 | // def extractStr(x: Exp[Any]): String 184 | 185 | def dump(out: java.io.PrintWriter)(x: Exp[Any]): Unit = { 186 | val idx = cstore groupBy { case IsTerm(id, _ , _) => id case _ => -1 } 187 | val stack = new mutable.BitSet(varCount) 188 | val stack2 = new mutable.BitSet(varCount) 189 | val seenVars = new mutable.HashMap[Int,Int] 190 | def canon(x: Exp[Any]): String = { 191 | val id = (Set(x.id) ++ (cstore collect { 192 | case IsEqual(`x`,y) if y.id < x.id => y.id 193 | case IsEqual(y,`x`) if y.id < x.id => y.id 194 | })).min 195 | val mid = seenVars.getOrElseUpdate(id,seenVars.size) 196 | "x"+mid 197 | } 198 | def rec(x: Exp[Any]): Unit = idx.getOrElse(x.id,Set.empty).headOption match { 199 | case Some(IsTerm(id, key, args)) => 200 | assert(id == x.id) 201 | if (stack.contains(id)) { 202 | out.print("r"+id) // not doing occurs check during unification, at least catch cycles here 203 | stack2 += id 204 | //return 205 | } 206 | stack += id 207 | // hack -- special case. don't print types. 208 | if (key == "lf") { 209 | rec(args.head) 210 | if (!idx.contains(args.head.id)) { 211 | out.print(":") 212 | rec(args.tail.head) 213 | } 214 | if (stack2.contains(id)) 215 | out.print("=r"+id) 216 | stack -= id 217 | stack2 -= id 218 | return 219 | } 220 | 221 | out.print(key) 222 | if (args.nonEmpty) { 223 | out.print("(") 224 | rec(args.head) 225 | args.tail.foreach { a => out.print(","); rec(a) } 226 | out.print(")") 227 | } 228 | if (stack2.contains(id)) { 229 | out.print("=r"+id) 230 | } 231 | stack -= id 232 | stack2 -= id 233 | case _ => 234 | out.print(canon(x)) 235 | } 236 | rec(x) 237 | out.flush 238 | } 239 | 240 | def extractStr(x: Exp[Any]): String = { 241 | val out = new java.io.ByteArrayOutputStream 242 | dump(new java.io.PrintWriter(new java.io.OutputStreamWriter(out)))(x) 243 | out.toString 244 | } 245 | } 246 | 247 | // TODO: this could be incorporated more nicely into extractStr? 248 | trait Prettify { 249 | class Str 250 | case class ConsStr(a: Str, b: Str) extends Str 251 | case object NilStr extends Str 252 | case class AtomStr(s: String) extends Str 253 | def balance(s: String): (String, String) = { 254 | var p = 0 255 | var i = 0 256 | var stop = false 257 | while (!stop && p >= 0 && i < s.length) { 258 | val c = s(i) 259 | i += 1 260 | if (c == '(') { p += 1 } 261 | else if (c == ')') { p -= 1 } 262 | else if (c == ',' && p == 0) { stop = true; i -= 1 } 263 | } 264 | (s.substring(0, i), s.substring(i)) 265 | } 266 | def structure(s: String): (Str, String) = { 267 | if (s.startsWith("cons(")) { 268 | val (a, ra) = structure(s.substring(5)) 269 | val (b, rb) = structure(ra.substring(1)) 270 | (ConsStr(a, b), rb.substring(1)) 271 | } else if (s.startsWith("nil")) { 272 | (NilStr, s.substring(3)) 273 | } else { 274 | val (a, r) = balance(s) 275 | (AtomStr(a), r) 276 | } 277 | } 278 | def addParen(p: (Boolean, String)) = { 279 | val (need_paren, s) = p 280 | if (need_paren) "("+s+")" else s 281 | } 282 | def pp(v: Str): (Boolean, String) = v match { 283 | case AtomStr(s) => (false, s) 284 | case NilStr => (true, "") 285 | case ConsStr(a, NilStr) => (true, addParen(pp(a))) 286 | case ConsStr(a, d) => 287 | val s1 = addParen(pp(a)) 288 | val (need_paren2, s2) = pp(d) 289 | if (need_paren2) (true, s1+" "+s2) 290 | else (true, s1+" . "+s2) 291 | } 292 | def pretty(s: String): String = { 293 | val (a, r) = structure(s) 294 | assert(r.isEmpty) 295 | val (_, p) = pp(a) 296 | p 297 | } 298 | def prettify(res: Seq[String]) = res.map(pretty) 299 | } 300 | -------------------------------------------------------------------------------- /src/main/scala/scalogno/meta.scala: -------------------------------------------------------------------------------- 1 | package scalogno 2 | 3 | trait MetaBase extends ListBase with Engine { 4 | trait Goal 5 | type Clause = (Exp[Goal], Exp[List[Goal]]) => Rel 6 | 7 | def existsC[T,U](f: (Exp[T],Exp[U]) => Clause): Clause = { 8 | f(fresh[T],fresh[U]) 9 | } 10 | } 11 | 12 | /* 13 | (define (vanilla* clause) 14 | (define (solve* goals) 15 | (conde 16 | ((== goals ’())) 17 | ((fresh (g gs body) 18 | (== (cons g gs) goals) 19 | (clause g body) 20 | (solve* body) 21 | (solve* gs))))) 22 | solve*) 23 | */ 24 | trait MetaVanilla extends MetaBase { 25 | def vanilla(clause: Clause)(goals: Exp[List[Goal]]): Rel = 26 | goals === nil || 27 | exists[Goal,List[Goal],List[Goal]] { (g, gs, body) => 28 | (goals === cons(g,gs)) && 29 | clause(g,body) && 30 | vanilla(clause)(body) && 31 | vanilla(clause)(gs) 32 | } 33 | } 34 | 35 | /* 36 | (define (tracer* clause) 37 | (define (solve* goals trace-in trace-out) 38 | (conde 39 | ((== goals '()) 40 | (== trace-in trace-out)) 41 | ((fresh (g gs body trace-out-body) 42 | (== (cons g gs) goals) 43 | (clause g body) 44 | (solve* body (cons g trace-in) trace-out-body) 45 | (solve* gs trace-out-body trace-out))))) 46 | (lambda (goal t) 47 | (solve* (list goal) '() t))) 48 | */ 49 | trait MetaTracer extends MetaBase { 50 | def tracer(clause: Clause)(in: Exp[List[Goal]], out: Exp[List[Goal]])(goals: Exp[List[Goal]]): Rel = 51 | ((goals === nil) && (in === out)) || 52 | exists[Goal,List[Goal],List[Goal],List[Goal]] { (g, gs, body, out_body) => 53 | (goals === cons(g,gs)) && 54 | clause(g,body) && 55 | tracer(clause)(cons(g,in),out_body)(body) && 56 | tracer(clause)(out_body,out)(gs) 57 | } 58 | 59 | 60 | } 61 | 62 | // APLAS 3.4 Clause Reification as Controlled Side Effect 63 | trait MetaReify extends MetaBase { 64 | def existsList(a: Int)(f: (List[Exp[Any]] => Rel)): Rel = { 65 | f((0 until a).map{_ => fresh[Any]}.toList) 66 | } 67 | 68 | // auto reification !!! 69 | 70 | var allclauses = Map[String,Clause]() 71 | val moregoals: DVar[Exp[List[Goal]]] = DVar(fresh) 72 | 73 | def ruleList(s: String)(a: Int)(f: List[Exp[Any]] => Rel): List[Exp[Any]] => Rel = { 74 | def goalTerm(xs: List[Exp[Any]]) = term[Goal](s,xs) 75 | allclauses += s -> 76 | { (head: Exp[Goal], body: Exp[List[Goal]]) => 77 | existsList(a) { xs => 78 | (head === goalTerm(xs)) && reifyGoals(f(xs))(body) 79 | } 80 | } 81 | 82 | {(xs: List[Exp[Any]]) => 83 | val hole = moregoals() 84 | moregoals := fresh 85 | hole === cons(goalTerm(xs),moregoals()) 86 | } 87 | } 88 | 89 | def rule[A,B](s: String)(f:(Exp[A], Exp[B]) => Rel): (Exp[A], Exp[B]) => Rel = { 90 | val g: List[Exp[Any]] => Rel = 91 | ruleList(s)(2)({ (xs: List[Exp[Any]]) => 92 | val List(xa, xb) = xs 93 | val a = xa.asInstanceOf[Exp[A]] 94 | val b = xb.asInstanceOf[Exp[B]] 95 | f(a, b) 96 | }) 97 | 98 | {(a: Exp[A], b: Exp[B]) => g(List(a, b)) } 99 | } 100 | 101 | def rule1[A](s: String)(f: Exp[A] => Rel): Exp[A] => Rel = { 102 | val g: List[Exp[Any]] => Rel = 103 | ruleList(s)(1)({ (xs: List[Exp[Any]]) => 104 | val List(xa) = xs 105 | val a = xa.asInstanceOf[Exp[A]] 106 | f(a) 107 | }) 108 | 109 | {(a: Exp[A]) => g(List(a)) } 110 | } 111 | 112 | def reifyGoals(goal: => Rel)(goals: Exp[List[Goal]]): Rel = { 113 | moregoals := goals 114 | goal && moregoals() === nil 115 | } 116 | 117 | def reifyClause(goal: => Rel)(head: Exp[Goal], body: Exp[List[Goal]]): Rel = { 118 | reifyGoals(goal)(cons(head,nil)) && { 119 | val s = extractStr(head) 120 | val key = s.substring(0,s.indexOf("(")) // a bit hacky, but hey ... 121 | println(key) 122 | allclauses(key)(head,body) 123 | } 124 | } 125 | 126 | def allclausesRel: Clause = { (head: Exp[Goal], body: Exp[List[Goal]]) => 127 | ((No:Rel) /: allclauses.values) ((r,c) => r || c(head,body)) 128 | } 129 | } 130 | 131 | trait MetaReifyVanilla extends MetaReify { 132 | def vanilla2(goal: => Rel): Rel = 133 | exists[List[Goal]] { goals => 134 | reifyGoals(goal)(goals) && vanilla2(goals) 135 | } 136 | 137 | def vanilla2(goals: Exp[List[Goal]]): Rel = { 138 | goals === nil || 139 | exists[Goal,List[Goal],List[Goal]] { (g, gs, body) => 140 | (goals === cons(g,gs)) && 141 | allclausesRel(g,body) && 142 | vanilla2(body) && 143 | vanilla2(gs) 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/main/scala/scalogno/tabling.scala: -------------------------------------------------------------------------------- 1 | package scalogno 2 | import scala.collection._ 3 | 4 | trait TablingBase extends Base with Engine { 5 | 6 | def memo(goal: Exp[Any])(a: => Rel): Rel 7 | 8 | def tabling(on: Boolean): Unit 9 | 10 | def dprintln(x: Any) = () // println(x) 11 | 12 | } 13 | 14 | trait TablingImpl extends TablingBase { self => 15 | // call table data structures and management 16 | type Answer = (Exp[Any] => Unit) 17 | case class Call(key: String, goal1: Exp[Any], cstore1: immutable.Set[Constraint], dvars1: immutable.Map[Int, Any], ldvars0: List[Exp[Any]], ldvars1: List[Exp[Any]], k1: Cont) { 18 | def load(ans: Answer): Unit = { 19 | // reset state to state at call 20 | cstore = cstore1; dvars = dvars1 21 | // equate actual state with symbolic before state 22 | dvarsEqu(ldvars0) 23 | // load constraints from answer 24 | ans(goal1); 25 | // update actual state to symbolic after state 26 | dvarsSet(ldvars1) 27 | } 28 | def equateStateAfterCall() = dvarsEqu(ldvars1) 29 | def updateStateBeforeCall() = dvarsSet(ldvars0) 30 | def makeAnswer() = self.makeAnswer(goal1) 31 | def table = contTable(key).reverse 32 | } 33 | 34 | val callTable = new mutable.HashMap[String, mutable.HashMap[String, Answer]] 35 | val contTable = new mutable.HashMap[String, List[Call]] 36 | 37 | var enabled = true 38 | 39 | def tabling(on: Boolean): Unit = { 40 | callTable.clear 41 | contTable.clear 42 | enabled = on 43 | } 44 | 45 | // save complete call state 46 | def makeCall(goal0: Exp[Any], k: Cont): Call = { 47 | val dvarsRange = (0 until dvarCount).toList 48 | val ldvars0 = dvarsRange.map(i => fresh[Any]) // symbolic state before call 49 | val ldvars1 = dvarsRange.map(i => fresh[Any]) // symbolic state for continuation / after call 50 | 51 | // extend goal with symbolic state before and after 52 | val goal = term("goal",List(goal0, term("state0", ldvars0), term("state1", ldvars1))) 53 | 54 | // but disregard state for memoization (compute key for goal0) 55 | val key = extractStr(goal0) 56 | val cont = Call(key,goal,cstore,dvars,ldvars0,ldvars1,k) 57 | contTable(key) = cont::contTable.getOrElse(key,Nil) 58 | cont 59 | } 60 | 61 | def makeAnswer(g1: Exp[Any]): Answer = { 62 | val lcstore = cstore 63 | val lidx = cstore groupBy { case IsTerm(id, _ , _) => id case _ => -1 } 64 | 65 | (g2: Exp[Any]) => { 66 | 67 | val stack = new mutable.BitSet(varCount) 68 | val seenVars = new mutable.HashMap[Int,Int] 69 | def copyVar(x: Exp[Any]): Exp[Any] = { 70 | val id = (Set(x.id) ++ (lcstore collect { 71 | case IsEqual(`x`,y) if y.id < x.id => y.id 72 | case IsEqual(y,`x`) if y.id < x.id => y.id 73 | })).min 74 | val mid = seenVars.getOrElseUpdate(id,seenVars.size) 75 | Exp(mid) 76 | } 77 | def copyTerm(x: Exp[Any]): Exp[Any] = lidx.getOrElse(x.id,Set.empty).headOption match { 78 | case Some(IsTerm(id, key, args)) => 79 | assert(id == x.id) 80 | assert(!stack.contains(id), "cyclic terms not handled") 81 | try { 82 | stack += id 83 | term(key, args map copyTerm) 84 | } finally { 85 | stack -= id 86 | } 87 | case _ => 88 | copyVar(x) 89 | } 90 | 91 | val g1_copy = copyTerm(g1) 92 | 93 | g1_copy === g2 94 | } 95 | } 96 | 97 | def dvarsSet(ls: List[Exp[Any]]) = { 98 | val dv = dvars 99 | dv foreach { case (k,v:Exp[Any]) => dvars += (k -> ls(k)) } 100 | } 101 | def dvarsEqu(ls: List[Exp[Any]]) = 102 | dvars foreach { case (k,v:Exp[Any]) => v === ls(k) } 103 | 104 | // tabling combinator 105 | def memo(goal0: Exp[Any])(a: => Rel): Rel = new Rel { 106 | override def exec(rec: Exec)(k: Cont): Unit = { 107 | if (!enabled) return rec(() => a)(k) 108 | 109 | def resume(cont: Call, ans: Answer) = rec{ () => cont.load(ans); Yes }(cont.k1) 110 | 111 | val cont = makeCall(goal0, k) 112 | callTable.get(cont.key) match { 113 | case Some(answers) => // call key found: 114 | // continue with stored answers 115 | for (ans <- answers.values) resume(cont, ans) 116 | case None => // call key not found: 117 | // add table entry 118 | val ansMap = new mutable.HashMap[String, Answer] 119 | callTable(cont.key) = ansMap 120 | rec { () => 121 | cont.updateStateBeforeCall() 122 | a // execute 123 | } { () => 124 | cont.equateStateAfterCall() 125 | val ansKey = extractStr(goal0) 126 | if (!ansMap.contains(ansKey)) { 127 | val ans = cont.makeAnswer() 128 | ansMap(ansKey) = ans // record new ansert and 129 | for (cont1 <- cont.table) { 130 | resume(cont1, ans) // resume stored continuations 131 | } 132 | } 133 | } 134 | } 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/test/scala/scalogno/core.scala: -------------------------------------------------------------------------------- 1 | package scalogno 2 | 3 | import org.scalatest.{Engine => _, _} 4 | import org.scalatest.funsuite.AnyFunSuite 5 | 6 | class MySuite extends AnyFunSuite { 7 | def expectResult(expected: Object)(actual: Object) = { 8 | assert(expected == actual) 9 | } 10 | } 11 | 12 | class TestNats extends MySuite with Base with Engine with NatBase with ListBase { 13 | 14 | test("lte") { 15 | expectResult(List("s(s(s(s(x0))))")) { 16 | run[Int] { q => 17 | lessThan(3, q) 18 | } 19 | } 20 | } 21 | 22 | } 23 | 24 | class TestLists extends MySuite with Base with Engine with NatBase with ListBase { 25 | 26 | test("append") { 27 | expectResult(List("cons(a,cons(b,cons(c,cons(d,cons(e,cons(f,nil))))))")) { 28 | run[List[String]] { q => 29 | append(list("a","b","c"), list("d","e","f"), q) 30 | } 31 | } 32 | expectResult(List("cons(d,cons(e,cons(f,nil)))")) { 33 | run[List[String]] { q => 34 | append(list("a","b","c"), q, list("a","b","c","d","e","f")) 35 | } 36 | } 37 | expectResult(List("cons(a,cons(b,cons(c,nil)))")) { 38 | run[List[String]] { q => 39 | append(q, list("d","e","f"), list("a","b","c","d","e","f")) 40 | } 41 | } 42 | expectResult(List( 43 | "pair(nil,cons(a,cons(b,cons(c,cons(d,cons(e,cons(f,nil)))))))", 44 | "pair(cons(a,nil),cons(b,cons(c,cons(d,cons(e,cons(f,nil))))))", 45 | "pair(cons(a,cons(b,nil)),cons(c,cons(d,cons(e,cons(f,nil)))))", 46 | "pair(cons(a,cons(b,cons(c,nil))),cons(d,cons(e,cons(f,nil))))", 47 | "pair(cons(a,cons(b,cons(c,cons(d,nil)))),cons(e,cons(f,nil)))", 48 | "pair(cons(a,cons(b,cons(c,cons(d,cons(e,nil))))),cons(f,nil))", 49 | "pair(cons(a,cons(b,cons(c,cons(d,cons(e,cons(f,nil)))))),nil)" 50 | )) { 51 | run[(List[String],List[String])] { q => 52 | val q1,q2 = fresh[List[String]] 53 | (q === pair(q1,q2)) && 54 | append(q1, q2, list("a","b","c","d","e","f")) 55 | } 56 | } 57 | } 58 | 59 | test("pair") { 60 | expectResult(List( 61 | "pair(nil,cons(a,cons(b,cons(c,cons(d,cons(e,cons(f,nil)))))))", 62 | "pair(cons(a,nil),cons(b,cons(c,cons(d,cons(e,cons(f,nil))))))", 63 | "pair(cons(a,cons(b,nil)),cons(c,cons(d,cons(e,cons(f,nil)))))", 64 | "pair(cons(a,cons(b,cons(c,nil))),cons(d,cons(e,cons(f,nil))))", 65 | "pair(cons(a,cons(b,cons(c,cons(d,nil)))),cons(e,cons(f,nil)))", 66 | "pair(cons(a,cons(b,cons(c,cons(d,cons(e,nil))))),cons(f,nil))", 67 | "pair(cons(a,cons(b,cons(c,cons(d,cons(e,cons(f,nil)))))),nil)" 68 | )) { 69 | run[(List[String],List[String])] { 70 | case Pair(q1,q2) => 71 | append(q1, q2, list("a","b","c","d","e","f")) 72 | } 73 | } 74 | expectResult(List("pair(x0,x0)")) { 75 | run[(List[String],List[String])] { 76 | case Pair(q1,q2) => q1 === q2 77 | } 78 | } 79 | } 80 | 81 | 82 | test("map") { 83 | expectResult(List("cons(a,cons(b,cons(c,cons(d,cons(e,cons(f,nil))))))")) { 84 | run[List[String]] { q => 85 | map[String,String]((u,v) => u === v, list("a","b","c","d","e","f"), q) 86 | 87 | } 88 | } 89 | expectResult(List("cons(A,cons(B,cons(C,cons(A,cons(B,cons(C,nil))))))")) { 90 | run[List[String]] { q => 91 | val elems = (List("a","b","c","d","e","f") zip List("A","B","C","A","B","C")): Exp[List[(String,String)]] 92 | map[String,String]((u,v) => contains(elems,(u,v)), list("a","b","c","d","e","f"), q) 93 | } 94 | } 95 | expectResult(List( 96 | "cons(a,cons(b,cons(a,nil)))", 97 | "cons(a,cons(b,cons(d,nil)))", 98 | "cons(a,cons(e,cons(a,nil)))", 99 | "cons(a,cons(e,cons(d,nil)))", 100 | "cons(d,cons(b,cons(a,nil)))", 101 | "cons(d,cons(b,cons(d,nil)))", 102 | "cons(d,cons(e,cons(a,nil)))", 103 | "cons(d,cons(e,cons(d,nil)))" 104 | )){ 105 | run[List[String]] { q => 106 | val elems = (List("a","b","c","d","e","f") zip List("A","B","C","A","B","C")): Exp[List[(String,String)]] 107 | map[String,String]((u,v) => contains(elems,(u,v)), q, list("A","B","A")) 108 | } 109 | } 110 | } 111 | 112 | /* 113 | test("mapf") { 114 | try { 115 | //delayedMode = true 116 | 117 | expectResult(List("cons(a,cons(b,cons(c,cons(d,cons(e,cons(f,nil))))))")) { 118 | run[List[String]] { q => 119 | val res = mapf[String,String](list("a","b","c","d","e","f"), (u,v) => u === v) 120 | q === res 121 | } 122 | } 123 | expectResult(List("cons(A,cons(B,cons(C,cons(A,cons(B,cons(C,nil))))))")) { 124 | run[List[String]] { q => 125 | val elems = (List("a","b","c","d","e","f") zip List("A","B","C","A","B","C")): Exp[List[(String,String)]] 126 | val res = mapf[String,String](list("a","b","c","d","e","f"), (u,v) => contains(elems,(u,v))) 127 | q === res 128 | } 129 | } 130 | expectResult(List( 131 | "cons(a,cons(b,cons(a,nil)))", 132 | "cons(a,cons(b,cons(d,nil)))", 133 | "cons(a,cons(e,cons(a,nil)))", 134 | "cons(a,cons(e,cons(d,nil)))", 135 | "cons(d,cons(b,cons(a,nil)))", 136 | "cons(d,cons(b,cons(d,nil)))", 137 | "cons(d,cons(e,cons(a,nil)))", 138 | "cons(d,cons(e,cons(d,nil)))" 139 | )){ 140 | run[List[String]] { q => 141 | val elems = (List("a","b","c","d","e","f") zip List("A","B","C","A","B","C")): Exp[List[(String,String)]] 142 | val res = mapf[String,String](q, (u,v) => contains(elems,(u,v))) 143 | res === list("A","B","A") 144 | } 145 | } 146 | 147 | } finally { 148 | //delayedMode = false 149 | } 150 | } 151 | */ 152 | 153 | test("flatMap") { 154 | expectResult(List("cons(a,cons(a,cons(b,cons(b,cons(c,cons(c,nil))))))")) { 155 | run[List[String]] { q => 156 | flatMap[String,String]((q1,q2) => q2 === cons(q1,cons(q1,nil)), list("a","b","c"), q) 157 | } 158 | } 159 | expectResult(List("cons(a,cons(b,cons(c,nil)))")) { 160 | run[List[String]] { q => 161 | flatMap[String,String]((q1,q2) => q2 === cons(q1,cons(q1,nil)), q, list("a","a","b","b","c","c")) 162 | } 163 | } 164 | } 165 | 166 | test("lessThan") { 167 | expectResult(List( 168 | "nil", 169 | "cons(z,nil)", 170 | "cons(z,cons(z,x0))", 171 | "cons(z,cons(s(z),nil))", 172 | "cons(z,cons(s(z),cons(z,x0)))", 173 | "cons(z,cons(s(z),cons(s(z),x0)))" 174 | )) { 175 | run[List[Int]] { q => 176 | lessLex[Int]((q1,q2) => lessThan(q1,q2), q, list(0,1,2)) 177 | } 178 | } 179 | } 180 | test("lessThanOrd") { 181 | expectResult(List( 182 | "nil", 183 | "cons(z,nil)", 184 | "cons(z,cons(z,x0))", 185 | "cons(z,cons(s(z),nil))", 186 | "cons(z,cons(s(z),cons(z,x0)))", 187 | "cons(z,cons(s(z),cons(s(z),x0)))" 188 | )) { 189 | run[List[Int]] { q => 190 | q < List(0,1,2) 191 | } 192 | } 193 | } 194 | } 195 | 196 | class TestTrees extends MySuite with Base with Engine with NatBase with ListBase with TreeBase { 197 | 198 | test("tree") { 199 | expectResult(List( 200 | "branch(branch(branch(nil,s(z),a,nil),s(s(z)),b,nil),s(s(s(z))),c,branch(nil,s(s(s(s(z)))),d,nil))" 201 | )) { 202 | run[Tree[Int,String]] { q => 203 | q === tree(1 -> "a", 2 -> "b", 3 -> "c", 4 -> "d") 204 | } 205 | } 206 | } 207 | 208 | test("lookupAll") { 209 | expectResult(List("c")) { 210 | run[String] { q => 211 | val t = tree(1 -> "a", 2 -> "b", 3 -> "c", 4 -> "d") 212 | lookupAll(t,3,q) 213 | } 214 | } 215 | } 216 | 217 | test("lookupOrd") { 218 | expectResult(List("c")) { 219 | run[String] { q => 220 | val t = tree(1 -> "a", 2 -> "b", 3 -> "c", 4 -> "d") 221 | lookup(t,3,q) 222 | } 223 | } 224 | expectResult(List("b")) { 225 | run[String] { q => 226 | val t = tree(List(1,1,1) -> "a", List(1,2,2) -> "b", List(2,1,1) -> "c", List(3,2,2) -> "d") 227 | lookup(t,List(1,2,2),q) 228 | } 229 | } 230 | expectResult(List("c")) { 231 | run[String] { q => 232 | val t = tree(List(1,1,1) -> "a", List(1,2,2) -> "b", List(2,1,1) -> "c", List(3,2,2) -> "d") 233 | lookup(t,List(2,1,1),q) 234 | } 235 | } 236 | expectResult(Nil) { 237 | run[String] { q => 238 | val t = tree(List(1,1,1) -> "a", List(1,2,2) -> "b", List(2,1,1) -> "c", List(3,2,2) -> "d") 239 | lookup(t,List(2,2,2),q) 240 | } 241 | } 242 | expectResult(List("a","b","c")) { 243 | run[String] { q => 244 | val t = tree(List(1,1,1) -> "a", List(1,2,2) -> "b", List(2,1,1) -> "c", List(3,2,2) -> "d") 245 | lookupLess(t,List(2,2,2),q) 246 | } 247 | } 248 | } 249 | 250 | test("lookup with holes") { 251 | expectResult(List("s(s(z))")) { 252 | run[Int] { q => 253 | val t = tree(List(1,1,1) -> "a", List(1,2,2) -> "b", cons(q,List(1,1)) -> inject("c"), List(3,2,2) -> "d") 254 | lookup(t,List(2,1,1),"c") 255 | } 256 | } 257 | expectResult(List("s(z)")) { 258 | run[Int] { q => 259 | val t = tree(List(1,1,1) -> "a", cons(q,List(2,2)) -> inject("b"), List(2,2,2) -> "c", List(3,2,2) -> "d") 260 | lookup(t,List(1,2,2),"b") 261 | } 262 | } 263 | } 264 | 265 | 266 | } 267 | 268 | 269 | trait TestGraphsBase extends MySuite with Base with Engine with NatBase with ListBase with GraphBase with ReifyUtilsBase { 270 | 271 | test("graph") { 272 | 273 | // APLAS 2.3 example 274 | val g = new Graph[String] { 275 | // APLAS 2.1 Relations as Functions 276 | def edge(x:Exp[String],y:Exp[String]) = 277 | (x === "a") && (y === "b") || 278 | (x === "b") && (y === "c") || 279 | (x === "c") && (y === "a") 280 | } 281 | 282 | // APLAS 2.1 edge example 283 | expectResult(List( 284 | "pair(a,b)", "pair(b,c)", "pair(c,a)" 285 | )) { 286 | run[(String,String)] { case Pair(q1,q2) => 287 | g.edge(q1,q2) 288 | } 289 | } 290 | // APLAS 2.1 path example 291 | expectResult(List( 292 | "b", "c", "a", "b", "c", "a", "b", "c", "a", "b" 293 | )) { 294 | runN[String](10) { q => 295 | g.path("a",q) 296 | } 297 | } 298 | 299 | trait HOGraph[T] { 300 | // APLAS 2.2 Higher-Order Relations as Higher-Order Functions 301 | def generic_path(edge: (Exp[T],Exp[T]) => Rel)(x: Exp[T], y: Exp[T]): Rel = edge(x,y) || exists[T] { z => edge(x,z) && generic_path(edge)(z,y) } 302 | } 303 | val hog = new HOGraph[String](){} 304 | 305 | expectResult(List( 306 | "b", "c", "a", "b", "c", "a", "b", "c", "a", "b" 307 | )) { 308 | runN[String](10) { q => 309 | hog.generic_path(g.edge)("a", q) 310 | } 311 | } 312 | 313 | trait RTC[T] { 314 | def refl_trans_clos(r: (Exp[T],Exp[T]) => Rel)(x: Exp[T], y: Exp[T]): Rel = r(x,y) || exists[T] { z => r(x,z) && refl_trans_clos(r)(z,y) } 315 | def refl_trans_closure(r: (Exp[T],Exp[T]) => Rel) = refl_trans_clos(r)_ 316 | } 317 | val rtc = new RTC[String](){} 318 | import rtc._ 319 | // APLAS 2.2 reflexive transitive closure 320 | val path = refl_trans_closure(g.edge) 321 | expectResult(List( 322 | "b", "c", "a", "b", "c", "a", "b", "c", "a", "b" 323 | )) { 324 | runN[String](10) { q => 325 | path("a", q) 326 | } 327 | } 328 | } 329 | 330 | test("trace") { 331 | val traceG = new Graph[String] { 332 | def edge(x:Exp[String],y:Exp[String]) = 333 | (x === "a") && (y === "b") || 334 | (x === "b") && (y === "c") || 335 | (x === "c") && (y === "a") 336 | 337 | // APLAS 3.3 path rule def example 338 | override def path(x:Exp[String],y:Exp[String]) = rule("path")(super.path)(x,y) 339 | } 340 | 341 | expectResult(List( 342 | "pair(b,cons(path(a,b),nil))", 343 | "pair(c,cons(path(b,c),cons(path(a,c),nil)))", 344 | "pair(a,cons(path(c,a),cons(path(b,a),cons(path(a,a),nil))))", 345 | "pair(b,cons(path(a,b),cons(path(c,b),cons(path(b,b),cons(path(a,b),nil)))))", 346 | "pair(c,cons(path(b,c),cons(path(a,c),cons(path(c,c),cons(path(b,c),cons(path(a,c),nil))))))" 347 | )) { 348 | // APLAS 3.3 path example 349 | runN[(String,List[List[String]])](5) { case Pair(q1,q2) => 350 | traceG.path("a",q1) && globalTrace() === q2 351 | } 352 | } 353 | } 354 | 355 | } 356 | 357 | class TestGraphs extends TestGraphsBase with ReifyUtils 358 | 359 | class TestGraphsDynVars extends TestGraphsBase with ReifyUtilsDynVars 360 | 361 | 362 | class TestMetaGraphs extends MySuite with Base with Engine with MetaGraphBase { 363 | 364 | val g = new Graph[String] { 365 | def edge(x:Exp[String],y:Exp[String]) = 366 | (x === "a") && (y === "b") || 367 | (x === "b") && (y === "c") || 368 | (x === "c") && (y === "a") 369 | } 370 | 371 | test("meta graph") { 372 | 373 | expectResult(List( 374 | "cons(to prove,cons(path(x0,x1),cons(prove,cons(cons(edge(x0,x1),nil),nil))))", 375 | "cons(to prove,cons(path(x0,x1),cons(prove,cons(cons(edge(x0,x2),cons(path(x2,x1),nil)),nil))))" 376 | )) { 377 | run[List[Any]] { q => 378 | exists[Goal,List[Goal]] { (head,body) => 379 | exists[String,String] { (a,b) => pathTerm(a,b) === head } && 380 | q === cons("to prove", cons(head, cons("prove", cons(body, nil)))) && 381 | pathFullClause1(g)(head,body) 382 | } 383 | } 384 | } 385 | 386 | expectResult(List( 387 | "cons(to prove,cons(path(a,b),cons(prove,cons(nil,nil))))", 388 | "cons(to prove,cons(path(b,c),cons(prove,cons(nil,nil))))", 389 | "cons(to prove,cons(path(c,a),cons(prove,cons(nil,nil))))", 390 | "cons(to prove,cons(path(a,x0),cons(prove,cons(cons(path(b,x0),nil),nil))))", 391 | "cons(to prove,cons(path(b,x0),cons(prove,cons(cons(path(c,x0),nil),nil))))", 392 | "cons(to prove,cons(path(c,x0),cons(prove,cons(cons(path(a,x0),nil),nil))))" 393 | )) { 394 | run[List[Any]] { q => 395 | exists[Goal,List[Goal]] { (head,body) => 396 | q === cons("to prove", cons(head, cons("prove", cons(body, nil)))) && 397 | pathClause1(g)(head,body) 398 | } 399 | } 400 | } 401 | 402 | expectResult(List( 403 | "cons(to prove,cons(path(a,b),cons(prove,cons(nil,nil))))", 404 | "cons(to prove,cons(path(b,c),cons(prove,cons(nil,nil))))", 405 | "cons(to prove,cons(path(c,a),cons(prove,cons(nil,nil))))", 406 | "cons(to prove,cons(path(a,x0),cons(prove,cons(cons(path(b,x0),nil),nil))))", 407 | "cons(to prove,cons(path(b,x0),cons(prove,cons(cons(path(c,x0),nil),nil))))", 408 | "cons(to prove,cons(path(c,x0),cons(prove,cons(cons(path(a,x0),nil),nil))))" 409 | )) { 410 | run[List[Any]] { q => 411 | val clause = existsC[String,String] { (a,b) => pathClause2(g)(a,b) } 412 | exists[Goal,List[Goal]] { (head,body) => 413 | clause(head,body) && q === cons("to prove", cons(head, cons("prove", cons(body, nil)))) 414 | } 415 | } 416 | } 417 | 418 | expectResult(List( 419 | "cons(to prove,cons(path(a,b),cons(prove,cons(nil,nil))))", 420 | "cons(to prove,cons(path(b,c),cons(prove,cons(nil,nil))))", 421 | "cons(to prove,cons(path(c,a),cons(prove,cons(nil,nil))))", 422 | "cons(to prove,cons(path(a,x0),cons(prove,cons(cons(path(b,x0),nil),nil))))", 423 | "cons(to prove,cons(path(b,x0),cons(prove,cons(cons(path(c,x0),nil),nil))))", 424 | "cons(to prove,cons(path(c,x0),cons(prove,cons(cons(path(a,x0),nil),nil))))" 425 | )) { 426 | run[List[Any]] { q => 427 | exists[Goal,List[Goal]] { (head,body) => 428 | q === cons("to prove", cons(head, cons("prove", cons(body, nil)))) && 429 | reifyClause(path2(g)(fresh,fresh))(head,body) 430 | } 431 | } 432 | } 433 | 434 | } 435 | 436 | 437 | test("graph interp") { 438 | 439 | expectResult(List( 440 | "b", "c", "a", "b", "c", "a", "b", "c", "a", "b" 441 | )) { 442 | runN[List[Any]](10) { q => 443 | vanilla(pathClause1(g))(cons(pathTerm("a",q),nil)) 444 | } 445 | } 446 | 447 | expectResult(List( 448 | "pair(b,cons(path(a,b),nil))", 449 | "pair(c,cons(path(b,c),cons(path(a,c),nil)))", 450 | "pair(a,cons(path(c,a),cons(path(b,a),cons(path(a,a),nil))))", 451 | "pair(b,cons(path(a,b),cons(path(c,b),cons(path(b,b),cons(path(a,b),nil)))))" 452 | )) { 453 | runN[(List[Any],List[Goal])](4) { case Pair(q,t) => 454 | tracer(pathClause1(g))(nil,t)(cons(pathTerm("a",q),nil)) 455 | } 456 | } 457 | 458 | } 459 | 460 | test("graph interp2") { 461 | 462 | expectResult(List( 463 | "b", "c", "a", "b", "c", "a", "b", "c", "a", "b" 464 | )) { 465 | runN[String](10) { q => 466 | vanilla2(path2(g)("a",q)) 467 | } 468 | } 469 | 470 | } 471 | 472 | 473 | } 474 | 475 | 476 | 477 | class TestSTLC extends MySuite with Base with Engine with STLC with STLC_ReverseDeBruijn with STLC_Nat { 478 | 479 | test("stlc1") { 480 | expectResult(List( 481 | "pair(cons(x0,nil),pair(z,x0))", 482 | "pair(cons(x0,cons(x1,nil)),pair(s(z),x0))", 483 | "pair(cons(x0,cons(x1,cons(x2,nil))),pair(s(s(z)),x0))" 484 | )) { 485 | runN[(Env,(Sym,LType))](3) { case Pair(g,Pair(x,tp)) => 486 | lookup(g,x,tp) 487 | } 488 | } 489 | 490 | expectResult(List( 491 | "pair(cons(x0,nil),pair(z,x0))", 492 | "pair(cons(x0,cons(x1,nil)),pair(s(z),x0))", 493 | "pair(cons(x0,cons(x1,cons(x2,nil))),pair(s(s(z)),x0))" 494 | )) { 495 | runN[(Env,(Sym,LType))](3) { case Pair(g,Pair(x,tp)) => 496 | val d1 = g |- sym(x) :: tp 497 | typecheck(d1) 498 | } 499 | } 500 | 501 | expectResult(List( 502 | "|-(cons(x0,cons(->(x0,x1),nil)),@(var(z),var(s(z))),x1)", 503 | "|-(cons(x0,cons(x1,cons(->(x1,x2),nil))),@(var(z),var(s(z))),x2)", 504 | "|-(cons(x0,cons(x1,cons(x2,cons(->(x2,x3),nil)))),@(var(z),var(s(z))),x3)" 505 | )) { 506 | runN[Deriv](3) { d => 507 | val a = fresh[Env] |- (sym(0) app sym(1)) :: fresh[LType] 508 | d === a && typecheck(d) 509 | } 510 | } 511 | 512 | expectResult(List( 513 | "|-(nil,lam(z,lam(s(z),@(var(z),var(s(z))))),->(->(x0,x1),->(x0,x1)))" 514 | )) { 515 | runN[Deriv](3) { d => 516 | val x,y = fresh[Sym] 517 | val a = nil |- lam(x, lam(y, (sym(x) app sym(y)))) :: fresh[LType] 518 | d === a && typecheck(d) 519 | } 520 | } 521 | 522 | expectResult(List( 523 | "|-(nil,lam(z,lam(s(z),+(var(z),var(s(z))))),->(nat,->(nat,nat)))" 524 | )) { 525 | runN[Deriv](3) { d => 526 | val x,y = fresh[Sym] 527 | val a = nil |- lam(x, lam(y, (sym(x) + sym(y)))) :: fresh[LType] 528 | d === a && typecheck(d) 529 | } 530 | } 531 | 532 | } 533 | } 534 | 535 | 536 | class TestXX extends MySuite with ListBase with NatBase with Engine with MetaGraphBase { 537 | 538 | trait LF 539 | 540 | val typ = term[LF]("type",Nil) 541 | 542 | def lf(x:Exp[LF],y:Exp[LF]) = term[Goal]("lf",List(x,y)) 543 | 544 | val nat = term[LF]("nat",Nil) 545 | 546 | val z = term[LF]("z",Nil) 547 | 548 | def s(x:Exp[LF]) = term[LF]("s",List(x)) 549 | 550 | def lte(x:Exp[LF],y:Exp[LF]) = term[LF]("lte",List(x,y)) 551 | 552 | def lte_z = term[LF]("lte_z",Nil) 553 | def lte_s(x:Exp[LF]) = term[LF]("lte_s",List(x)) 554 | 555 | def clauses(head: Exp[Goal], body: Exp[List[Goal]]): Rel = 556 | (head === lf(nat,typ)) && (body === nil) || // nat 557 | (head === lf(z,nat)) && (body === nil) || 558 | exists[LF] { x => 559 | (head === lf(s(x),nat)) && (body === cons(lf(x,nat),nil)) 560 | } || // lte 561 | exists[LF,LF] { (x,y) => 562 | (head === lf(lte(x,y),typ)) && (body === cons(lf(x,nat),cons(lf(y,nat),nil))) 563 | } || 564 | exists[LF,LF] { (x,y) => 565 | (head === lf(lte_z,lte(z,x))) && (body === nil) 566 | } || 567 | exists[LF,LF,LF] { (x,y,u) => 568 | (head === lf(lte_s(u),lte(s(x),s(y)))) && (body === cons(lf(u,lte(x,y)),nil)) 569 | } 570 | 571 | /* 572 | lte-trans : lte A B -> lte B C -> lte A C -> type. 573 | %mode lte-trans +A +B -C. 574 | 575 | -/z : lte-trans lte/z B lte/z. 576 | -/s : lte-trans (lte/s A) (lte/s B) (lte/s C) 577 | <- lte-trans A B C. 578 | 579 | %worlds () (lte-trans _ _ _). 580 | %total A (lte-trans A _ _). 581 | */ 582 | 583 | test("clause interp") { 584 | 585 | expectResult(List( 586 | "z", "s(z)", "s(s(z))" 587 | )) { 588 | runN[LF](3) { q => 589 | vanilla(clauses)(cons(lf(q,nat),nil)) 590 | } 591 | } 592 | 593 | expectResult(List( 594 | "pair(lte_s(lte_s(lte_z)),s(s(x0)))" 595 | )) { 596 | runN[(LF,LF)](3) { case Pair(q,n) => 597 | vanilla(clauses)(cons(lf(q,lte(s(s(z)),n)),nil)) 598 | } 599 | } 600 | 601 | 602 | } 603 | 604 | 605 | 606 | } 607 | 608 | 609 | class TestProb extends MySuite with ListBase with NatBase with Engine { 610 | 611 | implicit val injectBool = new Inject[Boolean] { 612 | def toTerm(x: Boolean): Exp[Boolean] = term(x.toString,Nil) 613 | } 614 | implicit val injectDouble = new Inject[Double] { 615 | def toTerm(x: Double): Exp[Double] = term(x.toString,Nil) 616 | } 617 | 618 | val theprob = DVar(1.0) 619 | 620 | def flip(p: Double, x: Exp[Boolean]): Rel = 621 | { theprob := theprob() * p; x === true } || 622 | { theprob := theprob() * (1.0 - p); x === false } 623 | 624 | def flip2(p: Double)(a: => Rel)(b: => Rel): Rel = 625 | { theprob := theprob() * p; a } || 626 | { theprob := theprob() * (1.0 - p); b } 627 | 628 | 629 | test("prob1") { 630 | 631 | expectResult(List( 632 | "pair(true,0.2)", 633 | "pair(false,0.8)" 634 | )) { 635 | runN[(Boolean,Double)](3) { case Pair(c,p) => 636 | flip(0.2,c) && { p === theprob() } 637 | } 638 | } 639 | 640 | expectResult(List( 641 | s"pair(true,${1.0*0.2*0.2})" 642 | )) { 643 | runN[(Boolean,Double)](3) { case Pair(c,p) => 644 | // what about call-time choice??? 645 | flip(0.2,c) && flip(0.2,c) && { c === true } && { p === theprob() } 646 | } 647 | } 648 | 649 | expectResult(List( 650 | "pair(true,0.2)" 651 | )) { 652 | runN[(Boolean,Double)](3) { case Pair(c,p) => 653 | flip2(0.2) { c === true } { c === false } && { c === true } && { p === theprob() } 654 | } 655 | } 656 | 657 | } 658 | 659 | } 660 | -------------------------------------------------------------------------------- /src/test/scala/scalogno/deriv.scala: -------------------------------------------------------------------------------- 1 | package scalogno 2 | 3 | class TestDeriv extends MySuite with Deriv with MetaSTLC with STLC_ReverseDeBruijn with Prettify 4 | { 5 | val g = new Graph[String] { 6 | def edge(x:Exp[String],y:Exp[String]) = 7 | (x === "a") && (y === "b") || 8 | (x === "b") && (y === "c") || 9 | (x === "c") && (y === "a") 10 | } 11 | 12 | test("deriv") { 13 | expectResult(List( 14 | "(path(a,b) <-- ())", 15 | "(path(a,c) <-- ((path(b,c) <-- ())))", 16 | "(path(a,a) <-- ((path(b,a) <-- ((path(c,a) <-- ())))))", 17 | "(path(a,b) <-- ((path(b,b) <-- ((path(c,b) <-- ((path(a,b) <-- ())))))))", 18 | "(path(a,c) <-- ((path(b,c) <-- ((path(c,c) <-- ((path(a,c) <-- ((path(b,c) <-- ())))))))))" 19 | )) { 20 | prettify(runN[List[List[Goal]]](5) { q => 21 | exists[List[Any]] { p => 22 | deriv(pathClause1(g))(q)(cons(pathTerm("a",p),nil)) 23 | } 24 | }) 25 | } 26 | } 27 | 28 | test("deriv_stlc") { 29 | expectResult(List( 30 | "(tc(|-(nil,lam(z,lam(s(z),@(var(z),var(s(z))))),->(->(x0,x1),->(x0,x1)))) <-- ((tc(|-(cons(->(x0,x1),nil),lam(s(z),@(var(z),var(s(z)))),->(x0,x1))) <-- ((tc(|-(cons(x0,cons(->(x0,x1),nil)),@(var(z),var(s(z))),x1)) <-- ((tc(|-(cons(x0,cons(->(x0,x1),nil)),var(z),->(x0,x1))) <-- ()) (tc(|-(cons(x0,cons(->(x0,x1),nil)),var(s(z)),x0)) <-- ())))))))" 31 | )) { 32 | prettify(runN[List[List[Goal]]](3) { q => 33 | val x,y = fresh[Sym] 34 | val a = nil |- lam(x, lam(y, (sym(x) app sym(y)))) :: fresh[LType] 35 | exists[List[Goal]] { goals => 36 | reifyGoals(typecheck(a))(goals) && 37 | deriv(allclausesRel)(q)(goals) 38 | } 39 | }) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/scala/scalogno/tabling.scala: -------------------------------------------------------------------------------- 1 | package scalogno 2 | 3 | import org.scalatest.{Engine => _, _} 4 | 5 | trait TestTablingAppBase extends MySuite with ListBase with NatBase with TablingBase with Engine { 6 | // APLAS 4.4 Definite Clause Grammar (DFG) 7 | def exp(s0: Exp[List[String]], s: Exp[List[String]]): Rel = memo(term("exp", List(s0,s))) { 8 | { val s1,s2 = fresh[List[String]] 9 | exp(s0,s1) && (s1 === cons("+",s2)) && trm(s2,s) } || 10 | trm(s0, s) } 11 | def trm(s0: Exp[List[String]], s: Exp[List[String]]): Rel = memo(term("trm", List(s0,s))) { 12 | { val s1,s2 = fresh[List[String]] 13 | trm(s0,s1) && (s1 === cons("*",s2)) && fct(s2,s) } || 14 | fct(s0, s) } 15 | def fct(s0: Exp[List[String]], s: Exp[List[String]]) = memo(term("fct", List(s0,s))) { 16 | { val s1,s2 = fresh[List[String]] 17 | s0 === cons("(", s1) && exp(s1, s2) && s2 === cons(")", s) } || 18 | { val n = fresh[String] 19 | s0 === cons(n, s) && dgt(n) } 20 | } 21 | def dgt(n: Exp[String]) = memo(term("dgt",List(n))) { 22 | n === "0" || n === "1" || n === "2" || n === "3" || n === "4" || 23 | n === "5" || n === "6" || n === "7" || n === "8" || n === "9" 24 | } 25 | 26 | def tokenize(s: String): List[String] = s.toList.map(_.toString) 27 | 28 | // APLAS 4.4 example 29 | test("dfg-ex") { 30 | expectResult(List("x0")) { 31 | run[List[String]] { q => 32 | tabling(true) 33 | exp(tokenize("3+4*7"), nil) 34 | } 35 | } 36 | } 37 | 38 | test("exp1") { 39 | expectResult(List()) { 40 | runN[List[String]](3) { q => 41 | tabling(true) 42 | exp(tokenize("3+4*"),nil) 43 | } 44 | } 45 | } 46 | 47 | test("exp2") { 48 | expectResult(List("x0")) { 49 | runN[List[String]](3) { q => 50 | tabling(true) 51 | exp(tokenize("3+4*5"),nil) 52 | } 53 | } 54 | } 55 | 56 | test("exp3") { 57 | expectResult(List("x0")) { 58 | runN[List[String]](3) { q => 59 | tabling(true) 60 | exp(tokenize("(3+4)*7"),nil) 61 | } 62 | } 63 | } 64 | 65 | def list8(q: Exp[List[String]]): Rel = memo(term("list8", List(q))) { 66 | val x1 = fresh[String] 67 | val x2 = fresh[String] 68 | val x3 = fresh[String] 69 | val x4 = fresh[String] 70 | val x5 = fresh[String] 71 | val x6 = fresh[String] 72 | val x7 = fresh[String] 73 | val x8 = fresh[String] 74 | q === cons(x1,cons(x2,cons(x3,cons(x4,cons(x5,cons(x6,cons(x7,cons(x8, nil)))))))) 75 | } 76 | 77 | test("exp4") { 78 | expectResult(List()) { 79 | runN[List[String]](3) { q => 80 | tabling(true) 81 | list8(q) && exp(q,nil) 82 | } 83 | } 84 | } 85 | } 86 | 87 | class TestTablingApp extends TestTablingAppBase with TablingImpl 88 | 89 | trait TestTablingBase extends MySuite with ListBase with NatBase with TablingBase with Engine { 90 | 91 | // APLAS 4 fib example 92 | def fib(x:Exp[Int], y:Exp[Int]): Rel = memo(term("fib",List(x,y))) { 93 | (x === 0) && (y === 1) || 94 | (x === 1) && (y === 1) || { 95 | val x1,x2,y1,y2 = fresh[Int] 96 | (x === succ(x1)) && (x === (succ(succ(x2)))) && 97 | fib(x1,y1) && fib(x2,y2) && 98 | add(y1,y2,y) 99 | } 100 | } 101 | 102 | test("fib1") { 103 | expectResult(List( 104 | "s(s(s(s(s(s(s(s(z))))))))" 105 | )) { 106 | runN[Int](3) { q => 107 | tabling(false) 108 | fib(5,q) 109 | } 110 | } 111 | println("done") 112 | } 113 | 114 | test("fib2") { 115 | expectResult(List( 116 | "s(s(s(s(s(s(s(s(z))))))))" 117 | )) { 118 | runN[Int](3) { q => 119 | tabling(true) 120 | fib(5,q) 121 | } 122 | } 123 | println("done") 124 | } 125 | } 126 | 127 | class TestTabling extends TestTablingBase with TablingImpl { 128 | 129 | def edge(x:Exp[String],y:Exp[String]) = 130 | (x === "a") && (y === "b") || 131 | (x === "b") && (y === "c") || 132 | (x === "c") && (y === "a") 133 | 134 | // APLAS 4.3 Example: Tabled Graph Evaluation 135 | 136 | def pathL(a: Exp[String], b: Exp[String]): Rel = memo(term("path",List(a,b))) { 137 | edge(a,b) || exists[String] { z => pathL(a,z) && { println("--"+extractStr(term("path-edge",List(a,z,b)))); edge(z,b) } } 138 | } 139 | def pathR(a: Exp[String], b: Exp[String]): Rel = memo(term("path",List(a,b))) { 140 | edge(a,b) || exists[String] { z => edge(a,z) && pathR(z,b) } 141 | } 142 | 143 | val globalTrace = DVar(nil: Exp[List[List[String]]]) 144 | 145 | def pathLT(a: Exp[String], b: Exp[String]): Rel = memo(term("path",List(a,b))) { 146 | globalTrace := cons(term("path",List(a,b)), globalTrace()) 147 | edge(a,b) || exists[String] { z => pathLT(a,z) && { println("--"+extractStr(term("path-edge",List(a,z,b)))); edge(z,b) } } 148 | } 149 | def pathRT(a: Exp[String], b: Exp[String]): Rel = memo(term("path",List(a,b))) { 150 | globalTrace := cons(term("path",List(a,b)), globalTrace()) 151 | edge(a,b) || exists[String] { z => edge(a,z) && pathRT(z,b) } 152 | } 153 | 154 | 155 | test("pathR") { 156 | expectResult(List( 157 | "b","c","a" 158 | )) { 159 | runN[String](5) { q1 => 160 | tabling(true) 161 | pathR("a",q1) 162 | } 163 | } 164 | println("done") 165 | } 166 | 167 | test("pathL") { 168 | expectResult(List( 169 | "b","c","a" 170 | )) { 171 | runN[String](5) { q1 => 172 | tabling(true) 173 | pathL("a",q1) 174 | } 175 | } 176 | println("done") 177 | } 178 | 179 | test("pathRT") { 180 | expectResult(List( 181 | "pair(b,cons(path(a,b),nil))", 182 | "pair(c,cons(path(b,c),cons(path(a,c),nil)))", 183 | "pair(a,cons(path(c,a),cons(path(b,a),cons(path(a,a),nil))))" 184 | )) { 185 | runN[(String,List[List[String]])](5) { case Pair(q1,q2) => 186 | tabling(true) 187 | pathRT("a",q1) && globalTrace() === q2 188 | } 189 | } 190 | println("done") 191 | } 192 | 193 | // APLAS 4.3 example 194 | test("pathLT") { 195 | expectResult(List( 196 | "pair(b,cons(path(a,b),nil))", 197 | "pair(c,cons(path(a,b),cons(path(a,c),nil)))", 198 | "pair(a,cons(path(a,b),cons(path(a,c),cons(path(a,a),nil))))" 199 | )) { 200 | runN[(String,List[List[String]])](5) { case Pair(q1,q2) => 201 | tabling(true) 202 | pathLT("a",q1) && globalTrace() === q2 203 | } 204 | } 205 | println("done") 206 | } 207 | 208 | } 209 | 210 | 211 | class TestTablingMore extends MySuite with ListBase with NatBase with TablingImpl with Engine { 212 | 213 | val accum = DVar(nil: Exp[List[String]]) 214 | def inc(n: Exp[Int]): Rel = { 215 | (n === 0) || exists[Int] { n1 => 216 | (n === succ(n1)) && { 217 | accum := cons("A", accum()) 218 | inc(n1) 219 | } 220 | } 221 | } 222 | 223 | def dlet[T](p: (DVar[T],T))(body: =>Rel): Rel = new Rel { 224 | override def exec(rec: Exec)(k: Cont): Unit = { 225 | val (v,x) = p 226 | val old = v() 227 | v := x 228 | rec(() => body) { () => v := old; k() } 229 | } 230 | } 231 | 232 | val last = DVar(nil: Exp[List[String]]) 233 | def inc2(n: Exp[Int]): Rel = { 234 | (n === 0) && (accum() === last()) || 235 | exists[Int] { n1 => 236 | (n === succ(n1)) && exists[List[String]] { tail => 237 | accum() === cons("A", tail) && dlet(accum -> tail) { 238 | inc2(n1) 239 | } 240 | } 241 | } 242 | } 243 | 244 | 245 | test("dletRel1") { 246 | expectResult(List( 247 | "pair(x0,cons(A,cons(A,cons(A,x0))))" 248 | )) { 249 | runN[(List[String],List[String])](5) { case Pair(q1,q2) => 250 | tabling(false) 251 | dlet(last -> q1) { 252 | dlet(accum -> q2) { 253 | inc2(3) 254 | } 255 | } 256 | } 257 | } 258 | } 259 | 260 | 261 | test("stateRel1") { 262 | expectResult(List( 263 | "pair(x0,cons(A,cons(A,cons(A,x0))))" 264 | )) { 265 | runN[(List[String],List[String])](5) { case Pair(q1,q2) => 266 | tabling(false) 267 | accum := q1 268 | inc(3) && accum() === q2 269 | } 270 | } 271 | } 272 | 273 | test("stateRel2") { 274 | expectResult(List( 275 | "pair(z,pair(x0,x0))", 276 | "pair(s(z),pair(x0,cons(A,x0)))", 277 | "pair(s(s(z)),pair(x0,cons(A,cons(A,x0))))", 278 | "pair(s(s(s(z))),pair(x0,cons(A,cons(A,cons(A,x0)))))", 279 | "pair(s(s(s(s(z)))),pair(x0,cons(A,cons(A,cons(A,cons(A,x0))))))" 280 | )) { 281 | runN[(Int,(List[String],List[String]))](5) { case Pair(q1,Pair(q2,q3)) => 282 | tabling(false) 283 | accum := q2 284 | inc(q1) && accum() === q3 285 | } 286 | } 287 | } 288 | 289 | test("stateRel3") { 290 | expectResult(List( 291 | "pair(z,cons(A,cons(A,cons(A,cons(A,nil)))))", 292 | "pair(s(z),cons(A,cons(A,cons(A,nil))))", 293 | "pair(s(s(z)),cons(A,cons(A,nil)))", 294 | "pair(s(s(s(z))),cons(A,nil))", 295 | "pair(s(s(s(s(z)))),nil)" 296 | )) { 297 | runN[(Int,List[String])](5) { case Pair(q1,q2) => 298 | tabling(false) 299 | accum := q2 300 | inc(q1) && accum() === List("A","A","A","A") 301 | } 302 | } 303 | } 304 | 305 | 306 | test("stateRel1T") { 307 | expectResult(List( 308 | "pair(x0,cons(A,cons(A,cons(A,x0))))" 309 | )) { 310 | runN[(List[String],List[String])](5) { case Pair(q1,q2) => 311 | tabling(true) 312 | accum := q1 313 | inc(3) && accum() === q2 314 | } 315 | } 316 | } 317 | 318 | test("stateRel2T") { 319 | expectResult(List( 320 | "pair(z,pair(x0,x0))", 321 | "pair(s(z),pair(x0,cons(A,x0)))", 322 | "pair(s(s(z)),pair(x0,cons(A,cons(A,x0))))", 323 | "pair(s(s(s(z))),pair(x0,cons(A,cons(A,cons(A,x0)))))", 324 | "pair(s(s(s(s(z)))),pair(x0,cons(A,cons(A,cons(A,cons(A,x0))))))" 325 | )) { 326 | runN[(Int,(List[String],List[String]))](5) { case Pair(q1,Pair(q2,q3)) => 327 | tabling(true) 328 | accum := q2 329 | inc(q1) && accum() === q3 330 | } 331 | } 332 | } 333 | 334 | test("stateRel3T") { 335 | expectResult(List( 336 | "pair(z,cons(A,cons(A,cons(A,cons(A,nil)))))", 337 | "pair(s(z),cons(A,cons(A,cons(A,nil))))", 338 | "pair(s(s(z)),cons(A,cons(A,nil)))", 339 | "pair(s(s(s(z))),cons(A,nil))", 340 | "pair(s(s(s(s(z)))),nil)" 341 | )) { 342 | runN[(Int,List[String])](5) { case Pair(q1,q2) => 343 | tabling(true) 344 | accum := q2 345 | inc(q1) && accum() === List("A","A","A","A") 346 | } 347 | } 348 | } 349 | } 350 | --------------------------------------------------------------------------------