diff options
author | Paul Phillips <paulp@improving.org> | 2010-09-18 00:09:07 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2010-09-18 00:09:07 +0000 |
commit | 49bdf3cda2095c781effe3b1aac69ad1e1791db7 (patch) | |
tree | cc7514c65164f4e67c64076065d763b8994cfcbe /src | |
parent | 513fd181bc99263c92f759c4e17889dcb8da53f0 (diff) | |
download | scala-49bdf3cda2095c781effe3b1aac69ad1e1791db7.tar.gz scala-49bdf3cda2095c781effe3b1aac69ad1e1791db7.tar.bz2 scala-49bdf3cda2095c781effe3b1aac69ad1e1791db7.zip |
Fixed long standing annoyance which prevents th...
Fixed long standing annoyance which prevents the same source file from
being used as both a script and a program. If a script has only a single
top level object with a main method, then that is the object to which
arguments will be passed. (Contrast with the current situation, where
a wrapper object is wrapped around it and the script will never do
anything since there is nothing which can reference the object.)
So now you can do this:
% cat script.scala
object FiddleDeeMain {
def main(args: Array[String]): Unit = {
println(args mkString " ")
}
}
% scala -nocompdaemon script.scala a b c
a b c
% scala script.scala a b c
a b c
% scalac script.scala
% scala FiddleDeeMain a b c
a b c
Closes #1173, review by community.
Diffstat (limited to 'src')
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/parser/Parsers.scala | 43 |
1 files changed, 43 insertions, 0 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index e44806139e..307745f8e5 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -199,6 +199,49 @@ self => val stmts = templateStatSeq(false)._2 accept(EOF) + def mainModuleName = settings.script.value + def searchForMain(): Option[Tree] = { + /** If there is only a single object template in the file and it has a + * suitable main method, we will use it rather than building another object + * around it. Since objects are loaded lazily the whole script would have + * been a no-op, so we're not taking much liberty. + */ + val MainName: Name = newTermName("main") + + /** Have to be fairly liberal about what constitutes a main method since + * nothing has been typed yet - for instance we can't assume the parameter + * type will look exactly like "Array[String]" as it could have been renamed + * via import, etc. + */ + def isMainMethod(t: Tree) = t match { + case DefDef(_, MainName, Nil, List(_), _, _) => true + case _ => false + } + /** For now we require there only be one top level object. */ + var seenModule = false + val newStmts = stmts collect { + case t @ Import(_, _) => t + case md @ ModuleDef(mods, name, template) if !seenModule && (md exists isMainMethod) => + seenModule = true + /** This slightly hacky situation arises because we have no way to communicate + * back to the scriptrunner what the name of the program is. Even if we were + * willing to take the sketchy route of settings.script.value = progName, that + * does not work when using fsc. And to find out in advance would impose a + * whole additional parse. So instead, if the actual object's name differs from + * what the script is expecting, we transform it to match. + */ + if (name.toString == mainModuleName) md + else treeCopy.ModuleDef(md, mods, mainModuleName, template) + case _ => + /** If we see anything but the above, fail. */ + return None + } + Some(makePackaging(0, emptyPkg, newStmts)) + } + + if (mainModuleName == ScriptRunner.defaultScriptMain) + searchForMain() foreach { return _ } + /** Here we are building an AST representing the following source fiction, * where <moduleName> is from -Xscript (defaults to "Main") and <stmts> are * the result of parsing the script file. |