diff options
author | Sean McDirmid <sean.mcdirmid@gmail.com> | 2007-04-19 05:16:02 +0000 |
---|---|---|
committer | Sean McDirmid <sean.mcdirmid@gmail.com> | 2007-04-19 05:16:02 +0000 |
commit | 2ef5d4c6d81e5008fdb2ae1b4f2ecdd9d9901fd2 (patch) | |
tree | 1d1b222bd6a0caa29c0a02e465842bdf30cfae52 /src/compiler/scala/tools/nsc/ast/parser/Scanners.scala | |
parent | e43c7bef06d64b98f00752bd06510768ba37910a (diff) | |
download | scala-2ef5d4c6d81e5008fdb2ae1b4f2ecdd9d9901fd2.tar.gz scala-2ef5d4c6d81e5008fdb2ae1b4f2ecdd9d9901fd2.tar.bz2 scala-2ef5d4c6d81e5008fdb2ae1b4f2ecdd9d9901fd2.zip |
Switching over to position objects from positio...
Switching over to position objects from position type parameters.
Positions are no longer ints.
Diffstat (limited to 'src/compiler/scala/tools/nsc/ast/parser/Scanners.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/parser/Scanners.scala | 505 |
1 files changed, 304 insertions, 201 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 33f0c7db71..4f6f06fb0b 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -8,34 +8,40 @@ package scala.tools.nsc.ast.parser import compat.StringBuilder import Tokens._ -import scala.tools.nsc.util.{Position, SourceFile} +import scala.tools.nsc.util.{Position, OffsetPosition, SourceFile} import SourceFile.{LF, FF, CR, SU} import scala.tools.nsc.util.CharArrayReader trait Scanners requires SyntaxAnalyzer { - import global._ - + abstract class AbstractTokenData { + def token : Int + type ScanPosition + val NoPos : ScanPosition + def pos : ScanPosition + def currentPos : ScanPosition + def name : Name + } /** A class for representing a token's data. */ - class TokenData { + trait TokenData extends AbstractTokenData { + type ScanPosition = Int - /** the next token */ - var token: int = EMPTY + val NoPos = -1 + /** the next token */ + var token: Int = EMPTY /** the token's position */ - var pos: int = 0 + var pos: Int = (0) + override def currentPos : Int = pos - 1 /** the first character position after the previous token */ - var lastPos: int = 0 - - def currentPos = pos - 1 - + var lastPos: Int = 0 /** the name of an identifier or token */ var name: Name = null /** the base of a number */ - var base: int = 0 + var base: Int = 0 def copyFrom(td: TokenData) = { this.token = td.token @@ -45,20 +51,198 @@ trait Scanners requires SyntaxAnalyzer { this.base = td.base } } + abstract class AbstractScanner extends AbstractTokenData { + implicit def p2g(pos : Position ) : ScanPosition + implicit def g2p(pos : ScanPosition) : Position + def configuration : ScannerConfiguration + def warning(pos : ScanPosition, msg : String) : Unit + def error (pos : ScanPosition, msg : String) : Unit + def incompleteInputError(pos : ScanPosition, msg : String) : Unit + def deprecationWarning(pos : ScanPosition, msg : String) : Unit + /** the last error position + */ + var errpos : ScanPosition + var lastPos : ScanPosition + def skipToken: ScanPosition + def nextToken: Unit + def next : AbstractTokenData + def intVal(negated: Boolean): Long + def floatVal(negated: Boolean): Double + def intVal : Long = intVal(false) + def floatVal : Double = floatVal(false) + //def token2string(token : Int) : String = configuration.token2string(token) + /* disabled in presentation compiler */ + var newNewLine : Boolean + /* disabled in presentation compiler */ + var skipping : Boolean + /** return recent scala doc, if any */ + def flushDoc : String + + } + + + trait ScannerConfiguration { +// Keywords ----------------------------------------------------------------- + /** Keyword array; maps from name indices to tokens */ + private var key: Array[byte] = _ + private var maxKey = 0 + private var tokenName = new Array[Name](128) + + { + var tokenCount = 0 + + // Enter keywords + + def enterKeyword(n: Name, tokenId: int): unit = { + while (tokenId >= tokenName.length) { + val newTokName = new Array[Name](tokenName.length * 2) + Array.copy(tokenName, 0, newTokName, 0, newTokName.length) + tokenName = newTokName + } + tokenName(tokenId) = n + if (n.start > maxKey) maxKey = n.start + if (tokenId >= tokenCount) tokenCount = tokenId + 1 + } + + enterKeyword(nme.ABSTRACTkw, ABSTRACT) + enterKeyword(nme.CASEkw, CASE) + enterKeyword(nme.CATCHkw, CATCH) + enterKeyword(nme.CLASSkw, CLASS) + enterKeyword(nme.DEFkw, DEF) + enterKeyword(nme.DOkw, DO) + enterKeyword(nme.ELSEkw, ELSE) + enterKeyword(nme.EXTENDSkw, EXTENDS) + enterKeyword(nme.FALSEkw, FALSE) + enterKeyword(nme.FINALkw, FINAL) + enterKeyword(nme.FINALLYkw, FINALLY) + enterKeyword(nme.FORkw, FOR) + enterKeyword(nme.IFkw, IF) + enterKeyword(nme.IMPLICITkw, IMPLICIT) + enterKeyword(nme.IMPORTkw, IMPORT) + enterKeyword(nme.MATCHkw, MATCH) + enterKeyword(nme.NEWkw, NEW) + enterKeyword(nme.NULLkw, NULL) + enterKeyword(nme.OBJECTkw, OBJECT) + enterKeyword(nme.OVERRIDEkw, OVERRIDE) + enterKeyword(nme.PACKAGEkw, PACKAGE) + enterKeyword(nme.PRIVATEkw, PRIVATE) + enterKeyword(nme.PROTECTEDkw, PROTECTED) + enterKeyword(nme.REQUIRESkw, REQUIRES) + enterKeyword(nme.RETURNkw, RETURN) + enterKeyword(nme.SEALEDkw, SEALED) + enterKeyword(nme.SUPERkw, SUPER) + enterKeyword(nme.THISkw, THIS) + enterKeyword(nme.THROWkw, THROW) + enterKeyword(nme.TRAITkw, TRAIT) + enterKeyword(nme.TRUEkw, TRUE) + enterKeyword(nme.TRYkw, TRY) + enterKeyword(nme.TYPEkw, TYPE) + enterKeyword(nme.VALkw, VAL) + enterKeyword(nme.VARkw, VAR) + enterKeyword(nme.WHILEkw, WHILE) + enterKeyword(nme.WITHkw, WITH) + enterKeyword(nme.YIELDkw, YIELD) + enterKeyword(nme.DOTkw, DOT) + enterKeyword(nme.USCOREkw, USCORE) + enterKeyword(nme.COLONkw, COLON) + enterKeyword(nme.EQUALSkw, EQUALS) + enterKeyword(nme.ARROWkw, ARROW) + enterKeyword(nme.LARROWkw, LARROW) + enterKeyword(nme.SUBTYPEkw, SUBTYPE) + enterKeyword(nme.VIEWBOUNDkw, VIEWBOUND) + enterKeyword(nme.SUPERTYPEkw, SUPERTYPE) + enterKeyword(nme.HASHkw, HASH) + enterKeyword(nme.ATkw, AT) + + // Build keyword array + key = new Array[byte](maxKey+1) + for (val i <- 0 to maxKey) + key(i) = IDENTIFIER + for (val j <- 0 until tokenCount) + if (tokenName(j) ne null) + key(tokenName(j).start) = j.asInstanceOf[byte] + + } +//Token representation ----------------------------------------------------- + + /** Convert name to token */ + def name2token(name: Name): int = + if (name.start <= maxKey) key(name.start) else IDENTIFIER + + /** Returns the string representation of given token. */ + def token2string(token: int): String = token match { + case IDENTIFIER | BACKQUOTED_IDENT => + "identifier"/* + \""+name+"\""*/ + case CHARLIT => + "character literal" + case INTLIT => + "integer literal" + case LONGLIT => + "long literal" + case FLOATLIT => + "float literal" + case DOUBLELIT => + "double literal" + case STRINGLIT => + "string literal" + case SYMBOLLIT => + "symbol literal" + case LPAREN => + "'('" + case RPAREN => + "')'" + case LBRACE => + "'{'" + case RBRACE => + "'}'" + case LBRACKET => + "'['" + case RBRACKET => + "']'" + case EOF => + "eof" + case ERROR => + "something" + case SEMI => + "';'" + case NEWLINE => + "';'" + case NEWLINES => + "';'" + case COMMA => + "','" + case CASECLASS => + "case class" + case CASEOBJECT => + "case object" + case XMLSTART => + "$XMLSTART$<" + case _ => + try { + "'" + tokenName(token) + "'" + } catch { + case _: ArrayIndexOutOfBoundsException => + "'<" + token + ">'" + case _: NullPointerException => + "'<(" + token + ")>'" + } + } + } + /** A scanner for the programming language Scala. * * @author Matthias Zenger, Martin Odersky, Burak Emir * @version 1.1 */ - class Scanner(unit: CompilationUnit) extends TokenData { - + abstract class Scanner extends AbstractScanner with TokenData { import Tokens._ import java.lang.{Integer, Long, Float, Double, Character} + override def intVal = super.intVal + override def floatVal = super.floatVal + override var errpos : Int = -1 - /** Character input reader - */ - val in = new CharArrayReader(unit.source.getContent(), !settings.nouescape.value, syntaxError) + val in : CharArrayReader /** character buffer for literals */ @@ -78,19 +262,29 @@ trait Scanners requires SyntaxAnalyzer { */ var docBuffer: StringBuilder = null + def flushDoc = { + val ret = if (docBuffer != null) docBuffer.toString else null; + docBuffer = null + ret + } + + + /** Process comments and strings in scanner */ + protected def matchInScanner = true + + /** add the given character to the documentation buffer */ protected def putDocChar(c: char): unit = if (docBuffer ne null) docBuffer.append(c) + private class TokenData0 extends TokenData { + } /** we need one token lookahead */ - val next = new TokenData() - val prev = new TokenData() + val next : TokenData = new TokenData0 + val prev : TokenData = new TokenData0 - /** the last error position - */ - var errpos = -1 /** a stack which indicates whether line-ends can be statement separators */ @@ -110,13 +304,13 @@ trait Scanners requires SyntaxAnalyzer { /** read next token and return last position */ - def skipToken(): int = { - val p = pos; nextToken() + def skipToken: Int = { + val p = pos; nextToken // XXX: account for off by one error //??? - p - 1 + (p - 1) } - def nextToken(): unit = { + def nextToken: Unit = { if (token == LPAREN) { sepRegions = RPAREN :: sepRegions } else if (token == LBRACKET) { @@ -136,7 +330,7 @@ trait Scanners requires SyntaxAnalyzer { } if (newNewLine && !skipping) { - unit.warning(pos, migrateMsg + "new line will start a new statement here;\n" + warning(pos, migrateMsg + "new line will start a new statement here;\n" + "to suppress it, enclose expression in parentheses (...)") newNewLine = false } @@ -148,7 +342,6 @@ trait Scanners requires SyntaxAnalyzer { this copyFrom next next.token = EMPTY } - if (token == CASE) { prev copyFrom this fetchToken() @@ -169,6 +362,13 @@ trait Scanners requires SyntaxAnalyzer { next copyFrom this this copyFrom prev } + } else if (token == IDENTIFIER && name == nme.MIXINkw) { //todo: remove eventually + prev.copyFrom(this) + fetchToken() + if (token == CLASS) + warning(prev.pos, "`mixin' is no longer a reserved word; you should use `trait' instead of `mixin class'"); + next.copyFrom(this) + this.copyFrom(prev) } if (afterLineEnd() && inLastOfStat(lastToken) && inFirstOfStat(token) && @@ -240,6 +440,23 @@ trait Scanners requires SyntaxAnalyzer { in.next getOperatorRest; // XXX return + + case '/' if !matchInScanner => + in.next + if (in.ch == '/') { + in.next; token = LINE_COMMENT + return + } else if (in.ch == '*') { + in.next + if (in.ch == '*') { + in.next; token = DOC_START + } else token = COMMENT_START + return + } else { + putChar('/') + getOperatorRest + return + } case '/' => in.next if (!skipComment()) { @@ -247,7 +464,6 @@ trait Scanners requires SyntaxAnalyzer { getOperatorRest return } - case '0' => putChar(in.ch) in.next @@ -264,10 +480,27 @@ trait Scanners requires SyntaxAnalyzer { base = 10 getNumber return + case '`' if !matchInScanner => + in.next; token = BACK_QUOTE + return case '`' => in.next getStringLit('`', BACKQUOTED_IDENT) return + case '\"' if !matchInScanner => + in.next + if (in.ch == '\"') { + in.next + if (in.ch == '\"') { + in.next; token = MULTI_QUOTE + } else { + token = EMPTY_STRING + } + return + } else { + token = DOUBLE_QUOTE + return + } case '\"' => in.next if (in.ch == '\"') { @@ -396,7 +629,8 @@ trait Scanners requires SyntaxAnalyzer { } } - private def skipComment(): boolean = + private def skipComment(): boolean = { + assert(matchInScanner) if (in.ch == '/') { do { in.next @@ -406,8 +640,9 @@ trait Scanners requires SyntaxAnalyzer { docBuffer = null var openComments = 1 in.next + val scalaDoc = ("/**", "*/") if (in.ch == '*' && onlyPresentation) - docBuffer = new StringBuilder("/**") + docBuffer = new StringBuilder(scalaDoc._1) while (openComments > 0) { do { do { @@ -434,6 +669,7 @@ trait Scanners requires SyntaxAnalyzer { } else { false } + } def inFirstOfStat(token: int) = token match { case EOF | CASE | CATCH | ELSE | EXTENDS | FINALLY | MATCH | REQUIRES | WITH | YIELD | @@ -500,7 +736,7 @@ trait Scanners requires SyntaxAnalyzer { return case SU => setName - token = name2token(name) + token = configuration.name2token(name) return case _ => if (java.lang.Character.isUnicodeIdentifierPart(in.ch)) { @@ -508,7 +744,7 @@ trait Scanners requires SyntaxAnalyzer { in.next } else { setName - token = name2token(name) + token = configuration.name2token(name) return } } @@ -525,20 +761,23 @@ trait Scanners requires SyntaxAnalyzer { in.next case '/' => in.next - if (skipComment()) { + if (matchInScanner && skipComment) { setName - token = name2token(name) + token = configuration.name2token(name) return - } else { - putChar('/') - } + } else if (!matchInScanner && (in.ch == '*' || in.ch == '/')) { + in.rewind + setName + token = configuration.name2token(name) + return + } else putChar('/') case _ => if (isSpecial(in.ch)) { putChar(in.ch) in.next } else { setName - token = name2token(name) + token = configuration.name2token(name) return } } @@ -557,11 +796,13 @@ trait Scanners requires SyntaxAnalyzer { if (isSpecial(in.ch)) getOperatorRest else { setName - token = name2token(name) + token = configuration.name2token(name) } } - private def getStringLit(delimiter: char, litType: int): unit = { + private def getStringLit(delimiter: char, litType: int): Unit = { + assert(matchInScanner) + //assert((litType==STRINGLIT) || (litType==IDENTIFIER)) while (in.ch != delimiter && (in.isUnicode || in.ch != CR && in.ch != LF && in.ch != SU)) { getlitch() } @@ -575,7 +816,8 @@ trait Scanners requires SyntaxAnalyzer { } } - private def getMultiLineStringLit: unit = + private def getMultiLineStringLit: Unit = { + assert(matchInScanner) if (in.ch == '\"') { in.next if (in.ch == '\"') { @@ -600,6 +842,7 @@ trait Scanners requires SyntaxAnalyzer { in.next getMultiLineStringLit } + } // Literals ----------------------------------------------------------------- @@ -716,7 +959,6 @@ trait Scanners requires SyntaxAnalyzer { } } - def intVal: long = intVal(false) /** convert name, base to double value */ @@ -724,7 +966,7 @@ trait Scanners requires SyntaxAnalyzer { val limit: double = if (token == DOUBLELIT) Double.MAX_VALUE else Float.MAX_VALUE try { - val value = Double.valueOf(name.toString()).doubleValue() + val value : double = Double.valueOf(name.toString()).doubleValue() if (value > limit) syntaxError("floating point number too large") if (negated) -value else value @@ -734,9 +976,6 @@ trait Scanners requires SyntaxAnalyzer { 0.0 } } - - def floatVal: double = floatVal(false) - /** read a number into name and set base */ protected def getNumber:unit = { @@ -779,7 +1018,7 @@ trait Scanners requires SyntaxAnalyzer { def xSync = { token = NEWLINE; // avoid getting NEWLINE from nextToken if last was RBRACE //in.next - nextToken() + nextToken } // Errors ----------------------------------------------------------------- @@ -787,7 +1026,7 @@ trait Scanners requires SyntaxAnalyzer { /** generate an error at the given position */ def syntaxError(pos: int, msg: String): unit = { - unit.error(pos, msg) + error(pos, msg) token = ERROR errpos = pos } @@ -798,159 +1037,11 @@ trait Scanners requires SyntaxAnalyzer { /** signal an error where the input ended in the middle of a token */ def incompleteInputError(msg: String): unit = { - unit.incompleteInputError(pos, msg) + incompleteInputError(pos, msg) token = EOF errpos = pos } -// Keywords ----------------------------------------------------------------- - - /** Keyword array; maps from name indices to tokens */ - private var key: Array[byte] = _ - private var maxKey = 0 - private var tokenName = new Array[Name](128) - - { - var tokenCount = 0 - - // Enter keywords - - def enterKeyword(n: Name, tokenId: int): unit = { - while (tokenId >= tokenName.length) { - val newTokName = new Array[Name](tokenName.length * 2) - Array.copy(tokenName, 0, newTokName, 0, newTokName.length) - tokenName = newTokName - } - tokenName(tokenId) = n - if (n.start > maxKey) maxKey = n.start - if (tokenId >= tokenCount) tokenCount = tokenId + 1 - } - - enterKeyword(nme.ABSTRACTkw, ABSTRACT) - enterKeyword(nme.CASEkw, CASE) - enterKeyword(nme.CATCHkw, CATCH) - enterKeyword(nme.CLASSkw, CLASS) - enterKeyword(nme.DEFkw, DEF) - enterKeyword(nme.DOkw, DO) - enterKeyword(nme.ELSEkw, ELSE) - enterKeyword(nme.EXTENDSkw, EXTENDS) - enterKeyword(nme.FALSEkw, FALSE) - enterKeyword(nme.FINALkw, FINAL) - enterKeyword(nme.FINALLYkw, FINALLY) - enterKeyword(nme.FORkw, FOR) - enterKeyword(nme.IFkw, IF) - enterKeyword(nme.IMPLICITkw, IMPLICIT) - enterKeyword(nme.IMPORTkw, IMPORT) - enterKeyword(nme.MATCHkw, MATCH) - enterKeyword(nme.NEWkw, NEW) - enterKeyword(nme.NULLkw, NULL) - enterKeyword(nme.OBJECTkw, OBJECT) - enterKeyword(nme.OVERRIDEkw, OVERRIDE) - enterKeyword(nme.PACKAGEkw, PACKAGE) - enterKeyword(nme.PRIVATEkw, PRIVATE) - enterKeyword(nme.PROTECTEDkw, PROTECTED) - enterKeyword(nme.REQUIRESkw, REQUIRES) - enterKeyword(nme.RETURNkw, RETURN) - enterKeyword(nme.SEALEDkw, SEALED) - enterKeyword(nme.SUPERkw, SUPER) - enterKeyword(nme.THISkw, THIS) - enterKeyword(nme.THROWkw, THROW) - enterKeyword(nme.TRAITkw, TRAIT) - enterKeyword(nme.TRUEkw, TRUE) - enterKeyword(nme.TRYkw, TRY) - enterKeyword(nme.TYPEkw, TYPE) - enterKeyword(nme.VALkw, VAL) - enterKeyword(nme.VARkw, VAR) - enterKeyword(nme.WHILEkw, WHILE) - enterKeyword(nme.WITHkw, WITH) - enterKeyword(nme.YIELDkw, YIELD) - enterKeyword(nme.DOTkw, DOT) - enterKeyword(nme.USCOREkw, USCORE) - enterKeyword(nme.COLONkw, COLON) - enterKeyword(nme.EQUALSkw, EQUALS) - enterKeyword(nme.ARROWkw, ARROW) - enterKeyword(nme.LARROWkw, LARROW) - enterKeyword(nme.SUBTYPEkw, SUBTYPE) - enterKeyword(nme.VIEWBOUNDkw, VIEWBOUND) - enterKeyword(nme.SUPERTYPEkw, SUPERTYPE) - enterKeyword(nme.HASHkw, HASH) - enterKeyword(nme.ATkw, AT) - - // Build keyword array - key = new Array[byte](maxKey+1) - for (val i <- 0 to maxKey) - key(i) = IDENTIFIER - for (val j <- 0 until tokenCount) - if (tokenName(j) ne null) - key(tokenName(j).start) = j.asInstanceOf[byte] - - } - -// Token representation ----------------------------------------------------- - - /** Convert name to token */ - def name2token(name: Name): int = - if (name.start <= maxKey) key(name.start) else IDENTIFIER - - /** Returns the string representation of given token. */ - def token2string(token: int): String = token match { - case IDENTIFIER | BACKQUOTED_IDENT => - "identifier"/* + \""+name+"\""*/ - case CHARLIT => - "character literal" - case INTLIT => - "integer literal" - case LONGLIT => - "long literal" - case FLOATLIT => - "float literal" - case DOUBLELIT => - "double literal" - case STRINGLIT => - "string literal" - case SYMBOLLIT => - "symbol literal" - case LPAREN => - "'('" - case RPAREN => - "')'" - case LBRACE => - "'{'" - case RBRACE => - "'}'" - case LBRACKET => - "'['" - case RBRACKET => - "']'" - case EOF => - "eof" - case ERROR => - "something" - case SEMI => - "';'" - case NEWLINE => - "';'" - case NEWLINES => - "';'" - case COMMA => - "','" - case CASECLASS => - "case class" - case CASEOBJECT => - "case object" - case XMLSTART => - "$XMLSTART$<" - case _ => - try { - "'" + tokenName(token) + "'" - } catch { - case _: ArrayIndexOutOfBoundsException => - "'<" + token + ">'" - case _: NullPointerException => - "'<(" + token + ")>'" - } - } - override def toString() = token match { case IDENTIFIER | BACKQUOTED_IDENT => "id(" + name + ")" @@ -975,12 +1066,24 @@ trait Scanners requires SyntaxAnalyzer { case COMMA => "," case _ => - token2string(token) + configuration.token2string(token) } /** INIT: read lookahead character and token. */ - in.next - nextToken() + def init = { + in.next + nextToken + } + } + class UnitScanner(unit : CompilationUnit) extends Scanner with ScannerConfiguration { + override def configuration = this + val in = new CharArrayReader(unit.source.getContent(), !settings.nouescape.value, syntaxError) + def warning(pos : Int, msg : String) = unit.warning(pos, msg) + def error (pos : Int, msg : String) = unit. error(pos, msg) + def incompleteInputError(pos : Int, msg : String) = unit.incompleteInputError(pos, msg) + def deprecationWarning(pos : Int, msg : String) = unit.deprecationWarning(pos, msg) + implicit def p2g(pos : Position) : Int = pos.offset.get(-1) + implicit def g2p(pos : Int ) : Position = new OffsetPosition(unit.source, pos) } } |