summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc
diff options
context:
space:
mode:
authorLex Spoon <lex@lexspoon.org>2007-01-14 19:44:59 +0000
committerLex Spoon <lex@lexspoon.org>2007-01-14 19:44:59 +0000
commit6f5749c79227212d4c3c1b9c1fd6aa9ec2bfe366 (patch)
tree99c936507922423a3201fff3d272149aea27f5fe /src/compiler/scala/tools/nsc
parentf3cf0544326e246cd6ecd1632ad43facc437e9d2 (diff)
downloadscala-6f5749c79227212d4c3c1b9c1fd6aa9ec2bfe366.tar.gz
scala-6f5749c79227212d4c3c1b9c1fd6aa9ec2bfe366.tar.bz2
scala-6f5749c79227212d4c3c1b9c1fd6aa9ec2bfe366.zip
Detect when a syntax error could be remedied
by adding more input to the end of a compilation unit. This is intended for use by the interpreter, so that it knows to keep reading more lines of input.
Diffstat (limited to 'src/compiler/scala/tools/nsc')
-rw-r--r--src/compiler/scala/tools/nsc/CompilationUnits.scala9
-rw-r--r--src/compiler/scala/tools/nsc/Interpreter.scala75
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala68
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Scanners.scala11
-rw-r--r--src/compiler/scala/tools/nsc/reporters/Reporter.scala16
5 files changed, 117 insertions, 62 deletions
diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala
index 8e2f9f14b7..4f2752b738 100644
--- a/src/compiler/scala/tools/nsc/CompilationUnits.scala
+++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala
@@ -12,6 +12,9 @@ import scala.collection.mutable.HashSet
trait CompilationUnits requires Global {
+ /** One unit of compilation that has been submitted to the compiler.
+ * It typically corresponds to a single file of source code. It includes
+ * error-reporting hooks. */
class CompilationUnit(val source: SourceFile) {
/** the fresh name creator */
@@ -42,6 +45,12 @@ trait CompilationUnits requires Global {
if (settings.unchecked.value) reporter.warning(position(pos), msg)
else currentRun.uncheckedWarnings = true
+ def incompleteInputError(pos:int, msg:String) =
+ if (!(errorPositions contains pos)) {
+ errorPositions += pos
+ reporter.incompleteInputError(position(pos), msg)
+ }
+
override def toString() = source.toString()
def clear() = {
diff --git a/src/compiler/scala/tools/nsc/Interpreter.scala b/src/compiler/scala/tools/nsc/Interpreter.scala
index aeb23ef8a5..e4dd4b8d6e 100644
--- a/src/compiler/scala/tools/nsc/Interpreter.scala
+++ b/src/compiler/scala/tools/nsc/Interpreter.scala
@@ -119,7 +119,7 @@ class Interpreter(val settings: Settings, reporter: Reporter, out: PrintWriter)
parentClassLoader)
/** XXX Let's get rid of this. I believe the Eclipse plugin is
- * the only user of it. */
+ * the only user of it, so this should be doable. */
protected def parentClassLoader: ClassLoader = null
/** the previous requests this interpreter has processed */
@@ -161,39 +161,41 @@ class Interpreter(val settings: Settings, reporter: Reporter, out: PrintWriter)
stringWriter.toString
}
- /** Parse a line into a sequence of trees.
- *
- * @param line ...
- * @return ...
- */
+ /** Parse a line into a sequence of trees. */
private def parse(line: String): List[Tree] = {
- // simple parse: just parse it, nothing else
- def simpleParse(code: String): List[Tree] = {
- val unit =
- new CompilationUnit(
- new SourceFile("<console>", code.toCharArray()))
-
- new compiler.syntaxAnalyzer.Parser(unit).templateStatSeq
- }
+ var justNeedsMore = false
+ reporter.withIncompleteHandler((pos,msg) => {justNeedsMore = true}) {
+//reporter.incompleteInputError = (pos,msg) => {justNeedsMore = true}
+ // simple parse: just parse it, nothing else
+ def simpleParse(code: String): List[Tree] = {
+ val unit =
+ new CompilationUnit(
+ new SourceFile("<console>", code.toCharArray()))
+ new compiler.syntaxAnalyzer.Parser(unit).templateStatSeq
+ }
- // parse the main code along with the imports
- reporter.reset
- val trees = simpleParse(codeForImports + line)
- if (reporter.hasErrors)
- Nil // the result did not parse, so stop
- else {
- // parse the imports alone
- val importTrees = simpleParse(codeForImports)
-
- // return just the new trees, not the import trees
- trees.drop(importTrees.length)
+ // parse the main code along with the imports
+ reporter.reset
+ val trees = simpleParse(codeForImports + line)
+ if (justNeedsMore) {
+ reporter.error(
+ null,
+ "Input truncated. Interpreted expressions must fit on one line.")
+ // XXX should accept more lines of input
+ Nil
+ } else if (reporter.hasErrors)
+ Nil // the result did not parse, so stop
+ else {
+ // parse the imports alone
+ val importTrees = simpleParse(codeForImports)
+
+ // return just the new trees, not the import trees
+ trees.drop(importTrees.length)
+ }
}
}
- /** Compile one source file.
- *
- * @param filename
- */
+ /** Compile one source file. */
def compileFile(filename: String): Unit = {
val jfile = new File(filename)
if (!jfile.exists) {
@@ -207,9 +209,6 @@ class Interpreter(val settings: Settings, reporter: Reporter, out: PrintWriter)
/** Compile an nsc SourceFile. Returns true if there are
* no compilation errors, or false othrewise.
- *
- * @param sources ...
- * @return ...
*/
def compileSources(sources: List[SourceFile]): Boolean = {
val cr = new compiler.Run
@@ -220,20 +219,11 @@ class Interpreter(val settings: Settings, reporter: Reporter, out: PrintWriter)
/** Compile a string. Returns true if there are no
* compilation errors, or false otherwise.
- *
- * @param code ...
- * @return ...
*/
def compileString(code: String): Boolean =
compileSources(List(new SourceFile("<script>", code.toCharArray)))
- /** build a request from the user. "tree" is "line" after being parsed.
- *
- * @param trees ...
- * @param line ...
- * @param lineName ...
- * @return ...
- */
+ /** build a request from the user. "trees" is "line" after being parsed. */
private def buildRequest(trees: List[Tree], line: String, lineName: String): Request =
trees match {
/* This case for assignments is more specialized than desirable: it only
@@ -252,7 +242,6 @@ class Interpreter(val settings: Settings, reporter: Reporter, out: PrintWriter)
case List(_:AliasTypeDef) => new TypeAliasReq(line, lineName)
case List(_:Import) => new ImportReq(line, lineName)
case _ => {
- //reporter.error(null, trees.toString)
reporter.error(null, "That kind of statement combination is not supported by the interpreter.")
null
}
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index d8ca5d73a1..f0a3808c01 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -137,14 +137,44 @@ trait Parsers requires SyntaxAnalyzer {
in.errpos = in.currentPos
}
+ def incompleteInputError(pos: int, msg: String): unit = {
+ if (pos != in.errpos) {
+ unit.incompleteInputError(pos, msg)
+ in.errpos = pos
+ }
+ }
+
+ def incompleteInputError(msg: String): unit =
+ incompleteInputError(in.currentPos, msg) // in.currentPos should be at the EOF
+
+ def syntaxErrorOrIncomplete(msg: String, skipIt: Boolean): unit = {
+ if(in.token == EOF)
+ incompleteInputError(msg)
+ else
+ syntaxError(in.currentPos, msg, skipIt)
+ }
+
+ /** Consume one token of the specified type, or
+ * signal an error if it is not there.
+ */
def accept(token: int): int = {
val pos = in.currentPos
- if (in.token != token)
- syntaxError(
- if (Position.line(unit.source, in.currentPos) > Position.line(unit.source, in.lastPos)) in.lastPos
- else in.currentPos,
+ if (in.token != token) {
+ val posToReport =
+ if (Position.line(unit.source, in.currentPos) >
+ Position.line(unit.source, in.lastPos))
+ in.lastPos
+ else
+ in.currentPos
+ val msg =
in.token2string(token) + " expected but " +
- in.token2string(in.token) + " found.", true)
+ in.token2string(in.token) + " found."
+
+ if(in.token == EOF)
+ incompleteInputError(posToReport, msg)
+ else
+ syntaxError(posToReport, msg, true)
+ }
if (in.token == token) in.nextToken()
pos
}
@@ -465,7 +495,7 @@ trait Parsers requires SyntaxAnalyzer {
case NULL =>
Constant(null)
case _ =>
- syntaxError("illegal literal", true)
+ syntaxErrorOrIncomplete("illegal literal", true)
null
})
}
@@ -879,7 +909,7 @@ trait Parsers requires SyntaxAnalyzer {
liftingScope(makeClosure(simpleExpr()))
// Note: makeClosure does some special treatment of liftedGenerators
} else {
- syntaxError("identifier expected", true)
+ syntaxErrorOrIncomplete("identifier expected", true)
errorTermTree
}
}
@@ -901,9 +931,9 @@ trait Parsers requires SyntaxAnalyzer {
Typed(t, atPos(pos1) { Ident(nme.WILDCARD_STAR.toTypeName) })
}
if (in.token != RPAREN)
- syntaxError(in.currentPos, "`)' expected", false)
+ syntaxErrorOrIncomplete("`)' expected", false)
} else {
- syntaxError(in.currentPos, "`*' expected", true)
+ syntaxErrorOrIncomplete("`*' expected", true)
}
} else {
t = atPos(pos) {
@@ -1032,6 +1062,8 @@ trait Parsers requires SyntaxAnalyzer {
t = atPos(pos) {
Function(ts.toList map convertToParam, TypeTree())
}
+ } else if(in.token == EOF) {
+ incompleteInputError("`=>' expected")
} else {
syntaxError(commapos, "`)' expected", false)
}
@@ -1063,7 +1095,7 @@ trait Parsers requires SyntaxAnalyzer {
else if (in.token == REQUIRES || in.token == IMPLICIT)
syntaxErrorMigrate(""+in+" is now a reserved word; cannot be used as identifier")
}
- syntaxError("illegal start of simple expression", true)
+ syntaxErrorOrIncomplete("illegal start of simple expression", true)
t = errorTermTree
}
simpleExprRest(t, isNew)
@@ -1338,7 +1370,7 @@ trait Parsers requires SyntaxAnalyzer {
if (settings.migrate.value &&
in.token == MATCH || in.token == REQUIRES || in.token == IMPLICIT)
syntaxErrorMigrate(""+in+" is now a reserved word; cannot be used as identifier")
- syntaxError("illegal start of simple pattern", true)
+ syntaxErrorOrIncomplete("illegal start of simple pattern", true)
errorPatternTree
}
@@ -1515,6 +1547,8 @@ trait Parsers requires SyntaxAnalyzer {
(!result.head.isEmpty && result.head.head.mods.hasFlag(Flags.IMPLICIT))))
if (in.token == LBRACKET)
syntaxError(pos, "no type parameters allowed here", false)
+ else if(in.token == EOF)
+ incompleteInputError(pos, "auxiliary constructor needs non-implicit parameter list")
else
syntaxError(pos, "auxiliary constructor needs non-implicit parameter list", false)
addImplicitViews(owner, result, implicitViews)
@@ -1847,7 +1881,7 @@ trait Parsers requires SyntaxAnalyzer {
case SUPERTYPE | SUBTYPE | SEMI | NEWLINE | COMMA | RBRACE =>
typeBounds(mods | Flags.DEFERRED, name)
case _ =>
- syntaxError("`=', `>:', or `<:' expected", true)
+ syntaxErrorOrIncomplete("`=', `>:', or `<:' expected", true)
EmptyTree
}
}
@@ -1868,7 +1902,7 @@ trait Parsers requires SyntaxAnalyzer {
case CASEOBJECT =>
objectDef(mods | Flags.CASE)
case _ =>
- syntaxError("illegal start of definition", true)
+ syntaxErrorOrIncomplete("expected start of definition", true)
EmptyTree
}
@@ -2011,7 +2045,7 @@ trait Parsers requires SyntaxAnalyzer {
(stats ++
joinAttributes(attrs, joinComment(List(tmplDef(modifiers()/*| mixinAttribute(attrs)*/)))))
} else if (in.token != SEMI && in.token != NEWLINE) {
- syntaxError("illegal start of class or object definition", true)
+ syntaxErrorOrIncomplete("expected class or object definition", true)
}
if (in.token != RBRACE && in.token != EOF) acceptStatSep()
}
@@ -2037,7 +2071,7 @@ trait Parsers requires SyntaxAnalyzer {
(stats ++
joinAttributes(attrs, joinComment(defOrDcl(modifiers()/*| mixinAttribute(attrs)*/))))
} else if (in.token != SEMI && in.token != NEWLINE) {
- syntaxError("illegal start of definition", true)
+ syntaxErrorOrIncomplete("illegal start of definition", true)
}
if (in.token != RBRACE && in.token != EOF) acceptStatSep()
}
@@ -2124,7 +2158,7 @@ trait Parsers requires SyntaxAnalyzer {
if (isDclIntro) {
stats ++= joinComment(defOrDcl(NoMods))
} else if (in.token != SEMI && in.token != NEWLINE) {
- syntaxError("illegal start of declaration", true)
+ syntaxErrorOrIncomplete("illegal start of declaration", true)
}
if (in.token != RBRACE) acceptStatSep()
}
@@ -2183,7 +2217,7 @@ trait Parsers requires SyntaxAnalyzer {
} else if (in.token == SEMI || in.token == NEWLINE) {
in.nextToken()
} else {
- syntaxError("illegal start of statement", true)
+ syntaxErrorOrIncomplete("illegal start of statement", true)
}
}
stats.toList
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
index d612148cfc..a1ed4b088d 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
@@ -440,7 +440,7 @@ trait Scanners requires SyntaxAnalyzer {
}
} while (in.ch != '/' && in.ch != SU)
if (in.ch == '/') in.next
- else syntaxError("unclosed comment")
+ else incompleteInputError("unclosed comment")
openComments = openComments - 1
}
true
@@ -606,7 +606,7 @@ trait Scanners requires SyntaxAnalyzer {
getMultiLineStringLit
}
} else if (in.ch == SU) {
- syntaxError("unclosed multi-line string literal")
+ incompleteInputError("unclosed multi-line string literal")
} else {
putChar(in.ch)
in.next
@@ -808,6 +808,13 @@ trait Scanners requires SyntaxAnalyzer {
*/
def syntaxError(msg: String): unit = syntaxError(pos, msg)
+ /** signal an error where the input ended in the middle of a token */
+ def incompleteInputError(msg: String): unit = {
+ unit.incompleteInputError(pos, msg)
+ token = EOF
+ errpos = pos
+ }
+
// Keywords -----------------------------------------------------------------
/** Keyword array; maps from name indices to tokens */
diff --git a/src/compiler/scala/tools/nsc/reporters/Reporter.scala b/src/compiler/scala/tools/nsc/reporters/Reporter.scala
index b75a75f565..cbfab874e4 100644
--- a/src/compiler/scala/tools/nsc/reporters/Reporter.scala
+++ b/src/compiler/scala/tools/nsc/reporters/Reporter.scala
@@ -47,4 +47,20 @@ abstract class Reporter {
def info(pos : Position, msg : String, force : Boolean) : Unit = info0(pos, msg, INFO , force);
def warning(pos : Position, msg : String ) : Unit = info0(pos, msg, WARNING, false);
def error(pos : Position, msg : String ) : Unit = info0(pos, msg, ERROR, false);
+
+ /** An error that could possibly be fixed if the unit were longer.
+ * This is used, for example, when the interpreter tries
+ * to distinguish fatal errors from those that are due to
+ * needing more lines of input from the user. */
+ var incompleteInputError: ((Position,String) => Unit) = error
+
+ def withIncompleteHandler[T](handler: ((Position,String) => Unit))(thunk: =>T) = {
+ val savedHandler = incompleteInputError
+ try {
+ incompleteInputError = handler
+ thunk
+ } finally {
+ incompleteInputError = savedHandler
+ }
+ }
}