summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/MainScript.scala
blob: 03aa9eaa8215988865b6433e1a959d33062cf0f5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/* NSC -- new Scala compiler
 * Copyright 2005-2006 LAMP/EPFL
 * @author  Martin Odersky
 */
// $Id: $

package scala.tools.nsc

import java.io.{BufferedReader,FileReader}

/** A main routine to support putting Scala code into scripts.
 *
 *  An shell script example on Unix would look like this:
 *
 *    #!/bin/sh
 *    exec scalascript "$0" "$@"
 *    !#
 *    Console.println("Hello, world!")
 *    argv.toList foreach Console.println
 *
 * A batch file example on Windows XP would look like this:
 *
 *    ::#!
 *    @echo off
 *    call scalascript %0 %*
 *    goto :eof
 *    ::!#
 *    Console.println("Hello, world!")
 *    argv.toList foreach Console.println
 *
 * TODO: It would be better if error output went to stderr instead
 * of stdout....
 */
object MainScript {
  /** Read the contents of the specified file, skipping the header
    * part if there is one.  The header part starts with "#!"
    * and ends with a line that begins with "!#".
    */
  val startRegexp = "^(::)?#!.*"
  val endRegexp = "^(::)?!#.*"

  def readFileSkippingHeader(filename: String): String = {
    val file =
      new BufferedReader(
          new FileReader(filename))
    val contents = new StringBuffer

    // skip the header, if there is one
    val firstLine = file.readLine
    if (firstLine == null)
      return ""
    else if (firstLine.matches(startRegexp)) {
      // skip until !# is seen
      def lp: Unit = {
        val line = file.readLine
        if (line == null)
          ()
        else if (!line.matches(endRegexp))
          lp
      }
      lp
    }
    else
      contents.append(firstLine)

    // now read the rest of the file
    def lp: Unit = {
      val line = file.readLine
      if (line == null)
        return ()
      contents.append(line)
      contents.append("\n")
      lp
    }
    lp

    contents.toString
  }

  /** Print a usage message and then exit. */
  def usageExit: Nothing = {
    Console.println(
        "scalascript [ compiler arguments... - ] scriptfile " +
        "[ script arguments... ]")
    Console.println
    Console.println(
        "Note that if you do specify compiler arguments, you \n" +
        "must put an explicit hyphen (\"-\") at the end of them.")
    System.exit(1).asInstanceOf[Nothing]
  }

  /** Parse an argument list into three portions:
    *
    *   Arguments to the compiler
    *   The filename of the script to run
    *   Arguments to pass to the script
    */
  def parseArgs(args: List[String])
      :Tuple3[List[String], String, List[String]] =
  {
    if (args.length == 0)
      usageExit

    if (args(0).startsWith("-")) {
      // the line includes compiler arguments
      val hyphenIndex = args.indexOf("-")
      if (hyphenIndex < 0)
        usageExit
      if (hyphenIndex == (args.length - 1))
        usageExit

      Tuple3(
        args.subseq(0, hyphenIndex).toList,
        args(hyphenIndex + 1),
        args.subseq(hyphenIndex + 2, args.length - hyphenIndex - 2).toList)
    } else {
      Tuple3(
        Nil,
        args(0),
        args.subseq(1, args.length-1).toList)
    }
  }

  def main(args: Array[String]): Unit = {
    val parsedArgs = parseArgs(args.toList)
    val compilerArgs = parsedArgs._1
    val scriptFile = parsedArgs._2
    val scriptArgs = parsedArgs._3.toArray

    val command =
      new CompilerCommand(
        compilerArgs,
        Console.println,
        false)

    if (!command.ok || command.settings.help.value) {
      // either the command line is wrong, or the user
      // explicitly requested a help listing
      if (!command.ok)
        Console.println
      usageExit
    }

    val scriptContents = readFileSkippingHeader(scriptFile)
    val toRun =
      "package scala.scripting\n" +
      "object Main {\n" +
      "  def main(argv: Array[String]): Unit = {\n" +
      scriptContents +
      "\n} }\n"

    val interpreter = new Interpreter(command.settings)
    interpreter.beQuiet
    if (!interpreter.compileString(toRun))
      return () // compilation error
    interpreter.bind("argv", "Array[String]", scriptArgs)
    interpreter.interpret("scala.scripting.Main.main(argv)")
  }

}