Skip to content

Commit 7791075

Browse files
committed
Add tagless interpreter test
1 parent f87554b commit 7791075

File tree

3 files changed

+194
-0
lines changed

3 files changed

+194
-0
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
3
2+
3
3+
4+
if (true) 3 else 4
5+
3
6+
7+
if (if (true) true else false) 3 else 4
8+
3
9+
10+
if (3 <= 7) 3 else 4
11+
3
12+
13+
if (3 <= 7) 3 + 4 else (5) * (2)
14+
7
15+
16+
(arg1 => arg1 + arg1)(4)
17+
8
18+
19+
(arg1 => if (arg1) 3 else 4)(true)
20+
3
21+
22+
if ((arg1 => arg1 <= arg1)(4)) 3 else 4
23+
3
24+
25+
if ((arg1 => arg1)(true)) 3 else 4
26+
3
27+
28+
(arg1 => arg1(4))((arg2 => arg2))
29+
4
30+
31+
(arg1 => (arg2 => (arg3 => if (arg3 <= 0) 1 else (arg1) * (arg2(arg3 + -1))))(FIX))(3)(25)
32+
1
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import scala.quoted._
2+
import scala.quoted.matching._
3+
4+
object Macros {
5+
6+
7+
inline def lift[R[_]](sym: Symantics { type Repr = R })(a: => Int): R[Int] = ${impl('sym, 'a)}
8+
9+
10+
private def impl[R[_]: Type](sym: Expr[Symantics { type Repr[X] = R[X] }], expr: Expr[Int]) given QuoteContext: Expr[R[Int]] = {
11+
12+
type Env = Map[Any, Any]
13+
14+
delegate ev0 for Env = Map.empty
15+
16+
def envWith[T](id: Bind[T], ref: Expr[R[T]]) given (env: Env): Env =
17+
env.updated(id, ref)
18+
19+
object FromEnv {
20+
def unapply[T](id: Bind[T]) given Env: Option[Expr[R[T]]] =
21+
the[Env].get(id).asInstanceOf[Option[Expr[R[T]]]] // We can only add binds that have the same type as the refs
22+
}
23+
24+
def lift[T: Type](e: Expr[T]) given (env: Env): Expr[R[T]] = ((e: Expr[Any]) match {
25+
case Const(e: Int) => '{ $sym.int(${e.toExpr}).asInstanceOf[R[T]] }
26+
case Const(e: Boolean) => '{ $sym.bool(${e.toExpr}).asInstanceOf[R[T]] }
27+
28+
case '{ ($x: Int) + ($y: Int) } =>
29+
'{ $sym.add(${lift(x)}, ${lift(y)}).asInstanceOf[R[T]] }
30+
31+
case '{ ($x: Int) * ($y: Int) } =>
32+
'{ $sym.mult(${lift(x)}, ${lift(y)}).asInstanceOf[R[T]] }
33+
34+
case '{ ($x: Int) <= ($y: Int) } =>
35+
'{ $sym.leq(${lift(x)}, ${lift(y)}).asInstanceOf[R[T]] }
36+
37+
case '{ ($f: $t => $u)($arg) } =>
38+
'{ $sym.app[$t, $u](${lift(f)}, ${lift(arg)}).asInstanceOf[R[T]] }
39+
40+
case '{ (if ($cond) $thenp else $elsep): $t } =>
41+
'{ $sym.ifThenElse[$t](${lift(cond)}, ${lift(thenp)}, ${lift(elsep)}) }.asInstanceOf[Expr[R[T]]]
42+
43+
case '{ ($x0: Int) => $body: Any } =>
44+
'{ $sym.lam((x: R[Int]) => ${delegate for Env = envWith(x0, 'x) given env; lift(body)}).asInstanceOf[R[T]] }
45+
case '{ ($x0: Boolean) => $body: Any } =>
46+
'{ $sym.lam((x: R[Boolean]) => ${delegate for Env = envWith(x0, 'x) given env; lift(body)}).asInstanceOf[R[T]] }
47+
case '{ ($x0: Int => Int) => $body: Any } =>
48+
'{ $sym.lam((x: R[Int => Int]) => ${delegate for Env = envWith(x0, 'x) given env; lift(body)}).asInstanceOf[R[T]] }
49+
50+
case '{ Symantics.fix[$t, $u]($f) } =>
51+
'{ $sym.fix[$t, $u]((x: R[$t => $u]) => $sym.app(${lift(f)}, x)).asInstanceOf[R[T]] }
52+
53+
case Bind(FromEnv(expr)) => expr.asInstanceOf[Expr[R[T]]]
54+
55+
case _ =>
56+
the[QuoteContext].error("Expected explicit value but got: " + e.show, e)
57+
'{ ??? }
58+
59+
})
60+
61+
lift(expr)
62+
}
63+
64+
}
65+
66+
trait Symantics {
67+
type Repr[X]
68+
def int(x: Int): Repr[Int]
69+
def bool(x: Boolean): Repr[Boolean]
70+
def lam[A, B](f: Repr[A] => Repr[B]): Repr[A => B]
71+
def app[A, B](f: Repr[A => B], arg: Repr[A]): Repr[B]
72+
def fix[A, B]: (Repr[A => B] => Repr[A => B]) => Repr[A => B]
73+
def add(x: Repr[Int], y: Repr[Int]): Repr[Int]
74+
def mult(x: Repr[Int], y: Repr[Int]): Repr[Int]
75+
def leq(x: Repr[Int], y: Repr[Int]): Repr[Boolean]
76+
def ifThenElse[A](cond: Repr[Boolean], thenp: => Repr[A], elsep: => Repr[A]): Repr[A]
77+
}
78+
79+
object Symantics {
80+
def fix[A, B](f: (A => B) => (A => B)): A => B = throw new Exception("Must be used inside of `lift`")
81+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import Macros._
2+
3+
object Test {
4+
5+
def main(args: Array[String]): Unit = {
6+
7+
println(lift(new Show)(3))
8+
println(lift(new Eval)(3))
9+
println()
10+
println(lift(new Show)(if (true) 3 else 4))
11+
println(lift(new Eval)(if (true) 3 else 4))
12+
println()
13+
println(lift(new Show)(if (if (true) true else false) 3 else 4))
14+
println(lift(new Eval)(if (if (true) true else false) 3 else 4))
15+
println()
16+
println(lift(new Show)(if (3 <= 7) 3 else 4))
17+
println(lift(new Eval)(if (3 <= 7) 3 else 4))
18+
println()
19+
println(lift(new Show)(if (3 <= 7) 3 + 4 else 5 * 2))
20+
println(lift(new Eval)(if (3 <= 7) 3 + 4 else 5 * 2))
21+
println()
22+
println(lift(new Show)(((x: Int) => x + x) (4)))
23+
println(lift(new Eval)(((x: Int) => x + x) (4)))
24+
println()
25+
println(lift(new Show)(((x: Boolean) => if (x) 3 else 4) (true)))
26+
println(lift(new Eval)(((x: Boolean) => if (x) 3 else 4) (true)))
27+
println()
28+
println(lift(new Show)(if (((x: Int) => x <= x)(4)) 3 else 4))
29+
println(lift(new Eval)(if (((x: Int) => x <= x)(4)) 3 else 4))
30+
println()
31+
println(lift(new Show)(if (((b: Boolean) => b)(true)) 3 else 4))
32+
println(lift(new Eval)(if (((b: Boolean) => b)(true)) 3 else 4))
33+
println()
34+
println(lift(new Show)(((f: Int => Int) => f(4))((x: Int) => x)))
35+
println(lift(new Eval)(((f: Int => Int) => f(4))((x: Int) => x)))
36+
println()
37+
println(lift(new Show)(((x: Int) => Symantics.fix((self: Int => Int) => ((n: Int) => if (n <= 0) 1 else x * self(n + (-1)) )))(3)(25)))
38+
println(lift(new Eval)(((x: Int) => Symantics.fix((self: Int => Int) => ((n: Int) => if (n <= 0) 1 else x * self(n + (-1)) )))(3)(5)))
39+
}
40+
41+
}
42+
43+
44+
class Show extends Symantics {
45+
type Repr[X] = String
46+
def int(x: Int): Repr[Int] = x.toString
47+
def bool(x: Boolean): Repr[Boolean] = x.toString
48+
def lam[A, B](f: Repr[A] => Repr[B]): Repr[A => B] = {
49+
val i = nextIndex()
50+
s"(arg$i => ${f(s"arg$i")})"
51+
}
52+
def app[A, B](f: Repr[A => B], arg: Repr[A]): Repr[B] = s"$f($arg)"
53+
def fix[A, B]: (Repr[A => B] => Repr[A => B]) => Repr[A => B] = f => f("FIX")
54+
def add(x: Repr[Int], y: Repr[Int]): Repr[Int] = s"$x + $y"
55+
def mult(x: Repr[Int], y: Repr[Int]): Repr[Int] = s"($x) * ($y)"
56+
def leq(x: Repr[Int], y: Repr[Int]): Repr[Boolean] = s"$x <= $y"
57+
def ifThenElse[A](cond: Repr[Boolean], thenp: => Repr[A], elsep: => Repr[A]): Repr[A] = s"if ($cond) $thenp else $elsep"
58+
59+
private[this] var idx: Int = 0
60+
private def nextIndex(): Int = {
61+
idx += 1
62+
idx
63+
}
64+
}
65+
66+
class Eval extends Symantics {
67+
type Repr[X] = X
68+
def int(x: Int): Repr[Int] = x
69+
def bool(x: Boolean): Repr[Boolean] = x
70+
def lam[A, B](f: Repr[A] => Repr[B]): Repr[A => B] = f
71+
def app[A, B](f: Repr[A => B], arg: Repr[A]): Repr[B] = f(arg)
72+
def fix[A, B]: (Repr[A => B] => Repr[A => B]) => Repr[A => B] = f => {
73+
def self(n: A): B = f(self)(n)
74+
self
75+
}
76+
def add(x: Repr[Int], y: Repr[Int]): Repr[Int] = x + y
77+
def mult(x: Repr[Int], y: Repr[Int]): Repr[Int] = x * y
78+
def leq(x: Repr[Int], y: Repr[Int]): Repr[Boolean] = x <= y
79+
def ifThenElse[A](cond: Repr[Boolean], thenp: => Repr[A], elsep: => Repr[A]): Repr[A] = if (cond) thenp else thenp
80+
81+
}

0 commit comments

Comments
 (0)