diff options
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 + } + } } |