Generating scala AST for recursive method - scala

I am generating the scala AST using the following code:
val setting = new Settings(error)
val reporter = new ConsoleReporter(setting, in, out) {
override def displayPrompt = ()
}
val compiler = new Global(setting, reporter) with ASTExtractor{
override def onlyPresentation = true
}
//setting.PhasesSetting("parser", "parserPhase")
val run = new compiler.Run
val sourceFiles:List[String] = List("Test.scala")
run.compile(sourceFiles.toList)
I guess this is the standard code used to run the compiler in the code and generate the AST to work with. The above code worked fine for any valid scala code in Test.scala till now. When I use a recursive function in Test.scala, like
def xMethod(x:Int):Int = if(x == 0) -1 else xMethod(x-1)
It gives me a java.lang.NullPointerException. The top few lines of the stack trace look like this
at scala.tools.nsc.typechecker.Typers$Typer.checkNoDoubleDefsAndAddSynthetics$1(Typers.scala:2170)
at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:2196)
at scala.tools.nsc.typechecker.Typers$Typer.typedBlock(Typers.scala:1951)
at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:3815)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:4124)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:4177)
at scala.tools.nsc.transform.TailCalls$TailCallElimination.transform(TailCalls.scala:199)
The code works fine for a method like
def aMethod(c:Int):Int = { bMethod(c) }
def bMethod(x:Int):Int = aMethod(x)
Please let me know if recursive functions need any other setting.

I can't tell you what you're doing wrong, but I use compiler.typedTree to get an AST in my project. Maybe this works for you as well.
See http://scala.ifs.hsr.ch/browser/src/scala/tools/refactoring/util/CompilerProvider.scala for more context.

Related

Scala Unit testing for ProcessAllWindowFunction

After Reading the official flink testing documentation (https://ci.apache.org/projects/flink/flink-docs-release-1.9/dev/stream/testing.html)
I was able to develop tests for a ProcessFunction, using a Test Harness, something like this:
pendingPartitionBuilder = new PendingPartitionBuilder(":::some_name", "")
testHarness =
new OneInputStreamOperatorTestHarness[StaticAdequacyTilePublishedData, PendingPartition](
new ProcessOperator[StaticAdequacyTilePublishedData, PendingPartition](pendingPartitionBuilder)
)
testHarness.open()
now, I’m trying to do the same for a ProcessAllWindowFunction, that looks like this:
class MapVersionValidationDistributor(batchSize: Int) extends
ProcessAllWindowFunction[MapVersionValidation, Seq[StaticAdequacyTilePublishedData],TimeWindow] {
lazy val state: ValueState[Long] = getRuntimeContext .getState(new ValueStateDescriptor[Long]("latestMapVersion", classOf[Long]))
(...)
First I realized I can’t use TestHarness for ProcessAllWindowFunction, because it doesn’t have a processElement method. In this case, what unit test strategy should I follow?
EDIT: At the moment my test code looks like this:
val collector = mock[Collector[Seq[StaticAdequacyTilePublishedData]]]
val mvv = new MapVersionValidationDistributor(1)
val input3 = Iterable(new MapVersionValidation("123",Seq(TileValidation(1,true,Seq(1,3,4)))))
val ctx = mock[mvv.Context]
val streamContext = mock[RuntimeContext]
mvv.setRuntimeContext(streamContext)
mvv.open(mock[Configuration])
mvv.process(ctx,input3,collector)
and I'm getting this error:
Unexpected call: <mock-3> RuntimeContext.getState[T](ValueStateDescriptor{name=latestMapVersion, defaultValue=null, serializer=null}) Expected: inAnyOrder { }
You don't really need test harness to unit test the process method of the ProcessAllWindowFunction. The process function takes 3 arguments: Context, Iterable[IN], Collector[OUT]. You can use some library depending on the language used to mock the Context. You can also easily implement or mock the Collector depending on your prerefences here. And the Iterable[IN] is just an Iterable containing the elements of Your window, that would be passed to the function after the window is triggered.

Why it is possible to write currying?

I have the following code:
object ContraCats {
val showString = Show[String]
def main(args: Array[String]): Unit = {
val m = showString.contramap[Symbol](_.name).show('dave)
val a = showString.contramap[Symbol](_.name)('dave)
}
}
As you can see, it is possible to write as currying version and the other as method call. Why it is possible?
The contramap returns a Show instance.
Show has both the show and the apply methods.
The apply method is special in Scala, since this two are equivalent:
someValue.apply(someArg)
someValue(someArg)
So in your example what's happening is that you're calling the apply method on the Show instance returned by contramap, i.e.
val m = showString.contramap[Symbol](_.name).show('dave)
val a = showString.contramap[Symbol](_.name).apply('dave)
Update
While the explanation above would make sense, I realized cats's Show doesn't have an apply method, so your code shouldn't compile (I tried on a REPL and it doesn't)

In a scala macro called with an implicit def, how can I get the source statement of the caller to the implicit def as a string?

I'm trying to write an implicit def macro that captures the invoking statement (and preferably nothing else) as a string, that can hopefully be used just by having the desired output object as an implicit parameter. But I'm having some trouble getting it to work with multi line statements.
For example:
case class HowWasICalled(statement:String, lineNumber:Int)
object HowWasICalled {
implicit def generate: HowWasICalled = macro macro_impl
}
def callMe(something: Any)(implicit context: HowWasICalled)
// macro: c is the blackbox Context
def macro_impl(c: blackbox.Context): c.Expr[HowWasICalled] = { ... }
And the first attempt at implementing a method inside the macro that returns the invoking statement as a string:
def makeString:String = show(c.macroApplication)
But, this only returned "HowWasICalled.generate". I actually wanted the statement invoking callMe.
I was hoping the following would work, but it doesn't. The -Yrangepos compiler flag does not seem to result in range positions in def macros? :(
def makeString:String = {
if (c.enclosingPosition.isRange) {
new String(c.enclosingPosition.source.content.slice(
c.enclosingPosition.start,
c.enclosingPosition.end
))
}
}
The closest I got to any working implementation doesn't exactly capture just statement nor the entire statement, but at least I get the line of source invoking callMe.
def makeString:String = {
val lineNumber = c.enclosingPosition.line
val lineIndex = lineNumber-1
c.enclosingPosition.source.lineToString(lineIndex)
}
How can I improve the macro to handle the following case? It should ideally generate something like a HowIWasCalled("callMe( 1 + 2 )", 123) given the following input
0; 0; 0; val x = callMe(1 +
2)

Run a script and get each lines result

I want to write a simple Scala script that runs some methods that are defined in another file.
Each line that runs this method requires information that won't be available until runtime. For simplicity sakes, I want to abstract that portion out.
Thus, I want to use Currying to get the result of each line in the script, then run the result again with the extra data.
object TestUtil {
// "control" is not known until runtime
def someTestMethod(x: Int, y: Int)(control: Boolean): Boolean = {
if (control) {
assert(x == y)
x == y
} else {
assert(x > y)
x > y
}
}
}
someTestMethod is defined in my primary codebase.
// testScript.sc
import <whateverpath>.TestUtil
TestUtil.someTestMethod(2,1)
TestUtil.someTestMethod(5,5)
Each line should return a function, that I need to rerun with a Boolean.
val control: Boolean = true
List[(Boolean) -> Boolean) testFuncs = runFile("testScript.sc")
testFuncs.foreach(_(control)) // Run all the functions that testScripts defined
(Sorry if this is a weird example, it's the simplest thing I can think of)
So far I have figured out how to parse the script, and get the Tree. However at that point I can't figure out how to execute each individual tree object and get the result. This is basically where I'm stuck!
val settings = new scala.tools.nsc.GenericRunnerSettings(println)
settings.usejavacp.value = true
settings.nc.value = true
val interpreter: IMain = new IMain(settings)
val treeResult: Option[List[Tree]] = interpreter.parse(
"""true
| 5+14""".stripMargin)
treeResult.get.foreach((tree: Tree) => println(tree))
the result of which is
true
5.$plus(14)
where 5 + 14 has not been evaluated, and I can't figure out how, or if this is even a worthwhile route to pursure
In the worst case you could toString your modified tree and then call interpreter.interpret:
val results = treeResult.get.map { tree: Tree => interpreter.interpret(tree.toString) }
It seems like it would be better to have the other file evaluate to something that can then be interpreted passing control (e.g. using the scalaz Reader Monad and for/yield syntax). But I assume you have good reasons for wanting to do this via an extra scala interpreter.

Compilation issue of a Scala worksheet in IntelliJ

I have a worksheet containing
object test {
val tableTest = new Array[String](1)
tableTest.length
}
that returns
tableTest: Array[String] = [Ljava.lang.String;#628c5fdf
res0: Int = 1
and it seems ok.
However, when I enter this worksheet:
object test {
val tableTest = new Array[String](1)
tableTest(0) = "zero"
}
IntelliJ cannot compile and returns me a Unable to read an event from: rO0ABXNyADdvcmcuamV0YnJhaW5zLmpwcy5pbmNyZW1lbnRhbC... error.
Did I do something wrong?
I'm having the same issue with latest Idea and Scala plugin.
It seems that the worksheet has a problem executing any line which evaluates to Unit. Assigning is Unit, that's why your tableTest(0) = "zero" fails.
I've temporarily solved it with the following workaround:
this line will fail with error Error:Unable to read an event from:...
println("Will fail")
You can fix it by defining this helper method and using it for any Unit expression:
def unit(f: => Unit): String = {f; ""}
unit(println("Will work"))
You just have to ignore the line it generates in the output panel with res0: String =
You also can put this method in some object and import in any WS you need.
Gaston.
#ktonga