summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2010-09-18 00:09:07 +0000
committerPaul Phillips <paulp@improving.org>2010-09-18 00:09:07 +0000
commit49bdf3cda2095c781effe3b1aac69ad1e1791db7 (patch)
treecc7514c65164f4e67c64076065d763b8994cfcbe /src
parent513fd181bc99263c92f759c4e17889dcb8da53f0 (diff)
downloadscala-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.scala43
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.