Skip to content

Commit 48bbffd

Browse files
committed
Rewort Interpreter
1 parent 487831c commit 48bbffd

File tree

4 files changed

+99
-53
lines changed

4 files changed

+99
-53
lines changed
Lines changed: 94 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
package dotty.tools.dotc
22
package interpreter
33

4+
import java.io.{PrintWriter, StringWriter}
5+
46
import dotty.tools.dotc.ast.tpd
57
import dotty.tools.dotc.ast.Trees._
68
import dotty.tools.dotc.core.Constants._
79
import dotty.tools.dotc.core.Contexts._
8-
import dotty.tools.dotc.core.Flags._
910
import dotty.tools.dotc.core.Decorators._
11+
import dotty.tools.dotc.core.Names._
1012
import dotty.tools.dotc.core.Symbols._
1113
import dotty.tools.dotc.quoted.Quoted
14+
import dotty.tools.dotc.util.Positions.Position
15+
1216
import scala.reflect.ClassTag
1317
import java.net.URLClassLoader
18+
import java.lang.reflect.Constructor
19+
import java.lang.reflect.Method
1420

1521
/** Tree interpreter that can interpret
1622
* * Literal constants
@@ -37,11 +43,14 @@ class Interpreter(implicit ctx: Context) {
3743
interpretTreeImpl(tree) match {
3844
case obj: T => Some(obj)
3945
case obj =>
46+
// TODO upgrade to a full type tag check or something similar
4047
ctx.error(s"Interpreted tree returned a result of an unexpected type. Expected ${ct.runtimeClass} but was ${obj.getClass}", tree.pos)
41-
throw new StopInterpretation
48+
None
4249
}
4350
} catch {
44-
case _: StopInterpretation => None
51+
case ex: StopInterpretation =>
52+
ctx.error(ex.msg, ex.pos)
53+
None
4554
}
4655
}
4756

@@ -50,56 +59,88 @@ class Interpreter(implicit ctx: Context) {
5059
*
5160
* If some error is encountered while interpreting a ctx.error is emited and a StopInterpretation is thrown.
5261
*/
53-
private def interpretTreeImpl(tree: Tree): Object = {
54-
try {
55-
tree match {
56-
case Quoted(quotedTree) => RawQuoted(quotedTree)
57-
58-
case Literal(Constant(c)) => c.asInstanceOf[AnyRef]
59-
60-
case Apply(fn, args) if fn.symbol.isConstructor =>
61-
val cls = fn.symbol.owner
62-
val clazz = classLoader.loadClass(cls.symbol.fullName.toString)
63-
val paramClasses = paramsSig(fn.symbol)
64-
val args1: List[Object] = args.map(arg => interpretTreeImpl(arg))
65-
clazz.getConstructor(paramClasses: _*).newInstance(args1: _*).asInstanceOf[Object]
66-
67-
case Apply(fun, args) if fun.symbol.isStatic =>
68-
val clazz = classLoader.loadClass(fun.symbol.owner.companionModule.fullName.toString)
69-
val paramClasses = paramsSig(fun.symbol)
70-
val args1: List[Object] = args.map(arg => interpretTreeImpl(arg))
71-
val method = clazz.getMethod(fun.symbol.name.toString, paramClasses: _*)
72-
method.invoke(null, args1: _*)
73-
74-
case tree: RefTree if tree.symbol.isStatic =>
75-
val clazz = classLoader.loadClass(tree.symbol.owner.companionModule.fullName.toString)
76-
val method = clazz.getMethod(tree.name.toString)
77-
method.invoke(null)
78-
79-
case tree: RefTree if tree.symbol.is(Module) =>
80-
??? // TODO
81-
82-
case Inlined(_, bindings, expansion) =>
83-
if (bindings.nonEmpty) ??? // TODO evaluate bindings and add environment
84-
interpretTreeImpl(expansion)
85-
case _ =>
86-
val msg =
87-
if (tree.tpe.derivesFrom(defn.QuotedExprClass)) "Quote needs to be explicit or a call to a static method"
88-
else "Value needs to be a explicit or a call to a static method"
89-
ctx.error(msg, tree.pos)
90-
throw new StopInterpretation
91-
}
92-
} catch {
93-
case ex: NoSuchMethodException =>
94-
ctx.error("Could not find interpreted method in classpath: " + ex.getMessage, tree.pos)
95-
throw new StopInterpretation
96-
case ex: ClassNotFoundException =>
97-
ctx.error("Could not find interpreted class in classpath: " + ex.getMessage, tree.pos)
98-
throw new StopInterpretation
62+
private def interpretTreeImpl(tree: Tree): Object = { // TODO add environment
63+
implicit val pos: Position = tree.pos
64+
tree match {
65+
case Quoted(quotedTree) => RawQuoted(quotedTree)
66+
67+
case Literal(Constant(c)) => c.asInstanceOf[AnyRef]
68+
69+
case Apply(fn, args) if fn.symbol.isConstructor =>
70+
val clazz = loadClass(fn.symbol.owner.symbol.fullName)
71+
val paramClasses = paramsSig(fn.symbol)
72+
val interpretedArgs = args.map(arg => interpretTreeImpl(arg))
73+
val constructor = getConstructor(clazz, paramClasses)
74+
interpreted(constructor.newInstance(interpretedArgs: _*))
75+
76+
case _: RefTree | _: Apply if tree.symbol.isStatic =>
77+
val clazz = loadClass(tree.symbol.owner.companionModule.fullName)
78+
val paramClasses = paramsSig(tree.symbol)
79+
80+
val interpretedArgs = Array.newBuilder[Object]
81+
def interpretArgs(tree: Tree): Unit = tree match {
82+
case Apply(fn, args) =>
83+
interpretArgs(fn)
84+
args.foreach(arg => interpretedArgs += interpretTreeImpl(arg))
85+
case _ =>
86+
}
87+
interpretArgs(tree)
88+
89+
val method = getMethod(clazz, tree.symbol.name, paramClasses)
90+
interpreted(method.invoke(null, interpretedArgs.result(): _*))
91+
92+
// case tree: RefTree if tree.symbol.is(Module) => // TODO
93+
// case Block(stats, expr) => // TODO evaluate bindings add environment
94+
// case ValDef(_, _, rhs) => // TODO evaluate bindings add environment
95+
96+
case Inlined(_, bindings, expansion) =>
97+
assert(bindings.isEmpty) // TODO evaluate bindings and add environment
98+
interpretTreeImpl(expansion)
99+
case _ =>
100+
// TODO Add more precise descriptions of why it could not be interpreted.
101+
// This should be done after the full interpreter is implemented.
102+
throw new StopInterpretation(s"Could not interpret ${tree.show}", tree.pos)
103+
}
104+
}
105+
106+
private def loadClass(name: Name)(implicit pos: Position): Class[_] = {
107+
try classLoader.loadClass(name.toString)
108+
catch {
109+
case _: ClassNotFoundException =>
110+
val msg = s"Could not find interpreted class $name in classpath"
111+
throw new StopInterpretation(msg, pos)
112+
}
113+
}
114+
115+
private def getMethod(clazz: Class[_], name: Name, paramClasses: List[Class[_]])(implicit pos: Position): Method = {
116+
try clazz.getMethod(name.toString, paramClasses: _*)
117+
catch {
118+
case _: NoSuchMethodException =>
119+
val msg = s"Could not find interpreted method ${clazz.getCanonicalName}.$name with parameters $paramClasses"
120+
throw new StopInterpretation(msg, pos)
121+
}
122+
}
123+
124+
private def getConstructor(clazz: Class[_], paramClasses: List[Class[_]])(implicit pos: Position): Constructor[Object] = {
125+
try clazz.getConstructor(paramClasses: _*).asInstanceOf[Constructor[Object]]
126+
catch {
127+
case _: NoSuchMethodException =>
128+
val msg = s"Could not find interpreted constructor of ${clazz.getCanonicalName} with parameters $paramClasses"
129+
throw new StopInterpretation(msg, pos)
130+
}
131+
}
132+
133+
private def interpreted[T](thunk: => T)(implicit pos: Position): T = {
134+
try thunk
135+
catch {
99136
case ex: RuntimeException =>
100-
ex.printStackTrace()
101-
ctx.error("A runtime exception occurred while interpreting: " + ex.getMessage, tree.pos)
102-
throw new StopInterpretation
137+
val sw = new StringWriter()
138+
sw.write("A runtime exception occurred while interpreting")
139+
sw.write(ex.getMessage)
140+
sw.write("\n")
141+
ex.printStackTrace(new PrintWriter(sw))
142+
sw.write("\n")
143+
throw new StopInterpretation(sw.toString, pos)
103144
}
104145
}
105146

@@ -114,6 +155,6 @@ class Interpreter(implicit ctx: Context) {
114155
}
115156

116157
/** Exception that stops interpretation if some issue is found */
117-
private class StopInterpretation extends Exception
158+
private class StopInterpretation(val msg: String, val pos: Position) extends Exception
118159

119160
}

tests/run/quote-and-splice.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
3
33
4
44
3
5+
5
56
1.0
67
5.0
78
25.0

tests/run/quote-and-splice/Macros_1.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ object Macros {
1111
inline def macro3(n: Int) = ~ macro3Impl('(n))
1212
def macro3Impl(p: Expr[Int]) = '{ 2 + ~p }
1313

14+
inline def macro4(i: Int)(j: Int) = ~ macro4Impl('(i))('(j))
15+
def macro4Impl(i: Expr[Int])(j: Expr[Int]) = '{ ~i + ~j }
16+
1417
inline def power(inline n: Int, x: Double) = ~powerCode(n, '(x))
1518

1619
def powerCode(n: Int, x: Expr[Double]): Expr[Double] =

tests/run/quote-and-splice/Test_2.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ object Test {
66
println(macro2(true))
77
println(macro2(false))
88
println(macro3(1))
9+
println(macro4(2)(3))
910
println(power(0, 5))
1011
println(power(1, 5))
1112
println(power(2, 5))

0 commit comments

Comments
 (0)